一、内部类
什么叫内部类?
将一个类A定义在另一个类B里面,里面的那个类A就称之为内部类。B则称之为外部类
- 成员内部类:定义在类中方法外的类
在描述事物时,若一个事物内部还包含其他事物,就可以使用内部类这种结构,比如汽车Car, 内部有发动机Engine;再比如实现一个AVL树,内部就可以定义一个Node类来表示节点,这个节点封装了此节点的相关属性和操作。
访问特点
- 内部类可以直接访问外部类的成员,包括私有成员(创建内部类,估计得先创建相应的外部类)
- 外部类要访问内部类的成员,必须要建立内部类的对象(类在方法区,必须在堆内存中创建实例对象才能访问!)
创建内部类对象格式:
外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
内部类任然是一个独立的类,在编译之后会将内部类编译成独立的.class文件,但是前面冠以外部类的类名和$符号。
示例:Car类
public class Car {
class Engine{
private String engineName;
private boolean sw;
public Engine(String engineName, boolean sw){
this.engineName = engineName;
this.sw = sw;
}
public String getCarName() { return name; }
}
private String name;
private Engine engin;
public Car(String name) { this.name = name; }
public void setEngin(Engine engin) { this.engin = engin; }
}
测试类
public class Main {
public static void main(String[] args) {
Car car = new Car("特斯拉");
Car.Engine engine = car.new Engine("牛逼发动机", true);
System.out.println(engine.getCarName());
car.setEngin(engine);
}
}
类的权限修饰符:public > protected > (default) > private
定义一个类的时候,权限修饰符规则:
- 外部类:public / (default)
- 成员内部类:public / protected / (default) / private
- 局部内部类:什么都不能写
- 局部内部类,如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】。备注:从Java 8+开始,只要局部变量事实不变,那么final关键字可以省略。
二、匿名内部类(重点)
匿名内部类:内部类的简写,本质是带有具体实现的,父类或者父接口的匿名的子类匿名内部类直接使用相当于创建了一个匿名内部对象,如果将匿名内部类交给一个变量,则创建的对象就不是匿名对象了。
使用一个接口的步骤,定义子类、重写、实例化、调用;而匿名内部类就是简化他们
前提:
匿名内部类必须继承一个父类或者实现一个父接口
匿名内部类本质就是一个对象,这个过程就是在一个类(或者主函数)中在new一个对象时先把类模板声明实现,再创建一个对象;不用先单独写一个.class实现父类或者接口,这样使用起来非常方便,匿名函数lamda表达式的原理应该就是匿名内部类。
格式:
父类或者接口名 obj = new 父类或者接口名(){
// 方法重写
@Override
public void method() {
// 执行语句
}
}
使用方式举例(一步到位)
Compare接口
public interface Compare<E> {
int compareTo(E a, E b);
}
Main主函数
public class Main {
public static void main(String[] args) {
// 一部到位高级玩二法
System.out.println(new Compare<Integer>() {
@Override
public int compareTo(Integer a, Integer b) {
return a - b;
}
}.compareTo(6, 10));
// 匿名函数
testFunction(100, 10, (a, b) -> a - b);
}
public static void testFunction(int a, int b, Compare<Integer> compare) {
System.out.println(compare.compareTo(a, b));
}
}
拓展:
- 我们使用一个接口,作为成员变量,以便随时更换技能,这样的设计更为灵活,增强了程序的扩展性。
- 接口作为成员变量时,对它进行赋值的操作,实际上,是赋给它该接口的一个子类对象。
- 接口作为参数时,传递它的子类对象。
- 接口作为返回值类型时,返回它的子类对象。