内部类这个地方比较容易混淆,还是要多总结、多是实战、多敲例子来熟悉、加深印象,从而熟练地掌握内部类的各种使用方法。
1、接口的定义:类似于class,其实就是一种特殊的类,而关键字是interface。
2、接口的默认定义:
- 接口中的属性默认是静态常量、而且访问权限是public。
- 接口中的方法默认都是抽象方法、而且访问权限是public。
注意点:接口中一般不写成员变量,只写方法,相当于制定规则,故而将接口成为方法列表。
3、接口的作用:让java从单继承间接的实现了多继承,扩充了功能,可以将之认为是类的补充。
4、接口起作用的方式:让类去实现接口
- 类与类之间的关系--继承 extends
- 类与接口之间的关系--实现 implements
5、一个类可以实现多个接口
注意点:如果两个借口定义了两个同名、同参数的方法、但是返回值不同,类则是没办法同时实心这两个接口。因为在同时实现的时候,无法最终确定这个方法的返回值类型。就好比一个人签订了两份合同,A合同要求性别男,B合同要求性别女。这是有冲突 的条件的,因此类无法同时实现。
代码例子:
interface CookService{
void cook();
}
interface NursemaidService{
void cook();
void wash();
}
class Nurse implements NursemaidService,CookService{
// 这个cook方法,在这两个接口中都定义了,那么只需要实现一次即可
@Override
public void cook() {
System.out.println("做饭");
}
@Override
public void wash() {
System.out.println("洗衣服");
}
}
6、接口的问题
- 父类与接口的功能如何分配?
答:一般父类存放的是主要功能,接口中放的是额外的功能,接口作为父类的补充。
- 当一个类实现的接口中出现了相同的方法,子类中实现方法的时候会不会混淆?
答:不会,接口中的方法都是抽象的,要通过子类来实现,接口中的方法只是声明,而我们在调用方法时候最终看的是功能,而功能在子类中。
- 接口与父类可以同时存在吗? 答:可以
- 一个子类只能有一个父类,也只能有一个接口吗?答:可以同时有多个接口。
- 接口是否可以直接创建对象?答:不可以,只能通过子类实现。
- 接口与接口之间是否可以有关系?如果有,会有什么关系? 答:可以有继承的关系,并且是多继承。
7、接口的向上向下转型类似于多态,就不用多加介绍了。
8、接口的子类重写方法注意事项:
- 如果一个类实现两个接口,这两个接口中同时有相同的抽象方法,则在类中只需要重写一次这个方法。
- 如果这两个接口中方法名、参数都相同的方法,其中一个接口是抽象方法、另一个是default修饰的有方法体。则该类也必须重写该方法。
9、接口的两个新特性,接上面的注意事项
- 接口中添加了static方法:可以在接口中定义静态方法,静态方法不是抽象方法,是有实现部分的。同时,这个静态方法,只能由当前的接口名字调用,接口的引用和实现类都是不能使用的。
package practice;
public class Demo4 {
public static void main(String[] args) {
MyInterface.method1(); //正确
MyInterface m = new Person();//接口中的静态方法,只能有当前接口的名字调用,接口的引用和实现类都不能使用的
// m.method1(); 错误
// Person.method1();错误
}
}
interface MyInterface {
public static void method1() {
System.out.println("接口中的静态方法实现");
}
}
class Person implements MyInterface{
//这里不可以重写method1方法
// @Override
// public static void method1(){
//
// }
}
- 接口中添加了default方法:修饰接口中的方法,default修饰的方法可以添加默认的实现部分。此时, 实现类在实现接口的时候,对于这些方法可以重写,也可以不重写。
package practice;
public class Demo5 {
public static void main(String[] args) {
MyInterface2 myInterface23 = new Person1();
myInterface23.method();
// Person.method2();//错误
}
}
interface MyInterface2{
public default void method(){
System.out.println("接口的方法默认实现");
}
}
class Person1 implements MyInterface2{
// @Override
// public void method() {
// System.out.println("重写的method");
// }
}
———————————————————————————————
1、内部类的分类
- 成员内部类
- 概念:其实就是定义在一个类中内部的类(大白话就是在类中在定义一个类)
注意:此内部类的地位与外部类的成员变量、成员方法,也可以把内部类看做成是外部类的成员,而成员 之间可以相互调用。而且成员内部类中,不能写静态属性、静态方法。
2. 成员内部类创建内部类对象
- 因为成员内部类是外部类的一个成员部分,所以创建内部类对象时候,必须依赖外部类对象。
- Outer outer = new Outer(); Inner inner = outer.new Inner();
- Inner inner = new Outer().new Inner(); //使用匿名外部类对象进行引用
3. 示例代码如下:
class OuterClass {
public String name;
public OuterClass(String name) {
this.name = name;
}
public class InnerClass {
public InnerClass(String name) {
this.name = name;
}
public String name;
public void show(String name) {
System.out.println(name); // 访问参数 name
System.out.println(this.name); // 访问内部类属性 name
System.out.println(OuterClass.this.name); // 访问外部类属性 name
}
}
public void test(){
System.out.println("Outer-show");
InnerClass inner = new InnerClass("构造函数");
inner.show("当前方法的值");
}
}
public class Demo6 {
public static void main(String[] args) {
// 实例化外部类对象
OuterClass outer = new OuterClass("外部类的值");
//调用内部类的方法的方式
//第一种:借助于外部类的方法实现
outer.test();
//方式二:借助外部类对象,实例化内部类对象
//引用:外部类.内部类
//构成:外部类对象的引用.new 内部类的构造方法
OuterClass.InnerClass innerClass = outer.new InnerClass("afaf");
OuterClass.InnerClass innerClass = new OuterClass.new InnerClass("afaf"); //用匿名外部类对象引用
}
}
- 静态内部类
1、概念:使用static修饰的类,在类的内部与实例变量、实例方法地位相同的。
注意点:静态内部类不一定有静态方法,但是有静态方法的一定是静态内部类。
2、成员内部类创建内部类对象
- 不依赖外部类对象,可以直接创建或通过类名访问。
- OuterClass.InnerClass inner = new OuterClass.InnerClass();
3、示例代码如下:
package practice;
public class OuterClass {
//静态内部类不一定有静态方法,有静态方法的一定是静态内部类 类似于抽象类的介绍
static class InnerClass {
String name;
public void show(String name) {
System.out.println(name);
System.out.println(this.name);
}
}
}
class Test {
public static void main(String[] args) {
// 1. 实例化静态内部类的对象
//构成: new + 外部类名字.内部类的构造方法
// OuterClass.InnerClass innerClass = new OuterClass.InnerClass();
// 2. 可以通过导包的形式,
// 先导包 import 包.OuterClass.InnerClass
Demo7.InnerClass innerClass = new Demo7.InnerClass();
innerClass.show("aaa");
}
}
总结1:成员内部类与静态内部的区别:第一点:成员内部类中不能写静态方法和静态属性;而静态内部类中可以写静态方法和静态属性。第二点:成员内部类对象的实例化需要借助外部类对象,而静态内部类的实例化不需要借助外部类对象。
- 局部内部类
1、概念: 局部内部类其实就是定义在外部类的方法中,作用范围和创建对象范围仅限于当前方法。
注意:局部内部类的变量必须是final修饰的常量;不能使用访问权限修饰符修饰(public、protected等修饰符不能修饰局部内部类);局部内部类对象只能在方法中进行实例化,从而利用外部类对象访问成员方法进行创建局部内部类对象。
2、示例代码如下
package practice;
public class Demo8 {
public static void main(String[] args) {
Outer1 outer1 = new Outer1();
outer1.show(); //输出 Inner-ruan=123
}
}
class Outer1{
public void show(){
//定义一个局部变量,如果这个局部变量被包裹(或者说在局部代码段中用到了此局部变量)在了一个局部代码段中(比如局部内部类、匿名内部类),此时这个局部变量被隐式的定义为final
int height=123;
class Inner{
public void run(){
System.out.println("Inner-run="+height);
}
}
//创建局部内部类对象
Inner inner = new Inner();
inner.run();
}
}
问题:为什么局部内部类访问外部类当前方法中的局部变量时,变量必须修饰为final?
答:因为无法保障变量的生命周期用自身相同,所以局部变量必须修饰为final。
3、局部内部类的作用:局部内部类实现了对方法的功能私有化,并且对方法内的代码进行了整理,增强了代码可读性和可操作性。
体现局部内部类对方法的功能私有化的 示例如下:
package practice;
public class Demo9 {
public void play(){
//如果我们想将这两个方法编程play方法的私有功能时,发现不能再play方法中直接写方法的定义,所以
//必须将之写入局部内部类中
/*public void printCar(){
System.out.println("打印汽车");
}*/
class Inner{
public void printCar(){
System.out.println("打印汽车");
}
public void printAnimal(){
System.out.println("打印动物");
}
}
Inner inner = new Inner();
inner.printAnimal();
inner.printCar();
}
public void show(){
//由于这两个方法是play的局部内部类中的方法,对play方法之外的方法都不可见
//因为局部内部类对其进行了功能的私有化
pirntCar(); //错误
printAnimal();//错误
}
}
注意点: 若是在外部类的方法中写入了局部内部类了,其实就是对外部类当前类的功能进行了私有化,仅仅只能当前类可以访问,其他的方法根本不能访问,请谨记这一点。
- 匿名内部类
1、概念:匿名内部类(对象)其实就是定义在一个类的方法中的匿名子类对象,也是局部内部类
提示:匿名内部类可以看成两部分:
匿名子类对象: 没有名字的子类的对象。
匿名内部类对象:定义在一个类中的方法中的匿名子类对象
2、注意点:
- 一切特点与局部内部类相同
- 必须继承一个父类或者实现一个接口。
- 进行了定义类、实现类、创建对象的语法合并,仅仅只能创建一个对象。
3、匿名内部类的作用:
- 无需实例化,只用到当前子类的一个实例对象的时候,定义好马上就用,只使用一次(被当做垃圾了、会被垃圾回收机制处理),使用完立马释放
- 当不太好起名字的时候使用匿名内部类
- 可以更好的定义运行时的回调(知道即可)
4、创建匿名子类对象(重点)
- 方式一: 使用已有的子类创建匿名子类对象
使用场景: 已经创建好的子类可以多次使用,使用于相同功能
- 方式二: 直接只用Animal创建匿名子类对象(重点)
构成: new + 父类的名字/接口的名字 + () + {写当前子类的成员} + ;
使用场景:只能使用一次,使用完会被当做垃圾回收,适用于每次都使用不同的功能
匿名子类对象的示例代码如下:
package task;
public class BigData08 {
public static void main(String[] args) {
Animal animal = new Animal();
new Animal().eat(); //匿名对象
//匿名子类对象
//第一种方式: 利用已有的子类创建匿名子类对象;
new Dog().eat();
//第二种方式: 直接使用Animal创建匿名子类对象,直接创建没有名字的Animal的匿名子类对象
new Animal(){
@Override
public void eat() {
System.out.println("Animal的匿名子类对象-eat");
}
}.eat();
}
}
class Animal{
public void eat(){
System.out.println("Animal-eat");
}
}
class Dog extends Animal{
public void eat(){
System.out.println("Dog-eat");
}
}
5、匿名内部类的注意点:
- 我们可以用匿名内部类作为方法的参数或返回值
- 匿名内部类的父类可以是父类也可以是父接口
- 除了new Object类是匿名对象,其他所有类的匿名对象本质上都是匿名子类对象。
示例代码如下:
package task;
public class BigData08 {
public static void main(String[] args) {
//测试匿名内部类
Test test1 = new Test();
test1.canShuTest();//task.Animal@1b6d3586
test1.canShuTest1();//task.Test$2@4554617c
}
}
class Animal{
public void eat(){
System.out.println("Animal-eat");
}
}
//研究匿名内部类
class Test{
public void show(){
//匿名内部类
new Animal(){
@Override
public void eat() {
System.out.println("匿名子类对象-eat");
}
}.eat();
}
//普通的匿名对象作为参数
public void canShuTest(){
System.out.println(new Animal());
}
//匿名内部类作为参数
public void canShuTest1(){
System.out.println(new Animal(){
@Override
public void eat() {
System.out.println("eat");
}
});
}
//普通的匿名对象作为返回值
public Animal fanHuiZhiTest() {
return new Animal();
}
//匿名内部类作为返回值
public Animal fanHuiZhiTest1(){
return new Animal(){
};
}
}