4. final关键字
final关键字可以修饰类、属性、方法、局部变量。
使用到final关键字的情况:
🐔 当不希望类被继承:
final class A { } //无法被继承
🐔 当不希望父类的某个方法被子类重写时
🐔 当不希望某个类中某个值被修改
public final int SIZE = 5 ;
🐔当不希望某个局部变量被修改
final double TAX_RATE = 0.08 ;
注意事项和使用细节:
1. final修饰的属性又叫常量,一般用 XX_XX_XX命名
2. final修饰的属性在定义时必须初始化,可选在如下位置:
(1)如:public final int SIZE = 5 ;
(2)构造器中;
(3)在代码块中;
3. 如果final修饰的属性是静态的,则被初始化的位置只能是
(1)定义时
(2)在静态代码块中,不能在构造器中赋值,因为实例对象才能调用构造器
4. final类不能被继承,但是可以实例化
5. final不能修饰构造方法
6. final和static往往搭配使用,效率更高,不会导致类加载,底层的编译器进行了优化处理。
5. 内部类
内部类是本章的重难点,在以后看源码的时候,会看见大量的内部类。
基本介绍:当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么这个内部的完整结构最好使用内部类。在 Java 中,可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。
基本语法:
class OutClass { //外部类 class InnerClass{ //内部类 } } class other { //外部其他类 }
特点: 既可以访问私有属性,又可以体现类与类之间的包含关系。
内部类的分类
定义在外部类局部位置上(比如方法内);
(1)局部内部类(有类名)
(2)匿名内部类(没有类名)
定义在外部类的成员位置上:
(1)实例内部类(没有static)
(2)静态内部类(使用static修饰)
1. 局部内部
说明:
1. 可以访问外部类的所有成员;
2. 不能添加访问修饰符,因为它的地位相当与局部变量,但是可以用final修饰
3. 作用域:仅仅只定义在它的方法或者代码段中
4. 在访问成员时直接调用即可
5. 外部类在方法中,可以创建内部类对象,然后调用方法即可;如:
public class Outer { public static void main(String[] args) { Outer outer = new Outer(); outer.n1(); } private int n1 = 100; private void m2() { System.out.println("2"); } public void n1() { final class Inner { public void f1() { System.out.println("1"); m2(); } } Inner inner = new Inner(); inner.f1(); } }
6. 如果外部类和局部内部类成员发生重名时,默认遵守就近原则,如果向想访问外部类的成员,可以使用(外部类名.this.成员名)
2. 匿名内部类
本质:
(1)本质仍是一个类
(2)是个内部类
(3)从宏观上来来书评该类没有名字,但是在底层上被赋予了一个名字
(4)同时还是个对象
说明:匿名内部类定义在外部类的局部位置,譬如方法体,但是没有名字
语法:
new 类或者接口(参数列表){ 类体 };
例如:
class Outer { public static void main(String[] args) { Outer outer = new Outer(); outer.method(); } private int m = 10; public void method() { //需求:使用一个IA接口,并创建一个对象 //传统写法:写一个类,实现该接口 IA tiger = new Tiger(); tiger.cry(); } } interface IA { public void cry(); } class Tiger implements IA { @Override public void cry() { System.out.println("嗷嗷嗷嗷"); } }
但是如果我只用一次,后面就不再使用,这里单独写一个Tiger时不时太浪费了,所以我们可以试着用匿名内部类写。
class Outer { public static void main(String[] args) { Outer outer = new Outer(); outer.method(); } private int m = 10; public void method() { //需求:使用一个IA接口,并创建一个对象 //传统写法:写一个类,实现该接口 //现在我们不要Tiger类 IA tiger = new IA() { @Override public void cry() { System.out.println("嗷嗷嗷嗷"); } }; tiger.cry(); } } interface IA { public void cry(); }
本来我们的接口是不可以直接实例化对象的,但这里就相当于重写了cry()方法,并没有实例化它的对象。
class Outer { public static void main(String[] args) { Outer outer = new Outer(); outer.method(); } private int m = 10; public void method() { //需求:使用一个IA接口,并创建一个对象 //传统写法:写一个类,实现该接口 //现在我们不要Tiger类 //使用匿名内部类来简化代码 //此时的编译类型是:IA接口 //此时的运行类型是:匿名内部类 /* 底层: class XXXXXXX(类名) implements IA { @Override public void cry() { System.out.println("嗷嗷嗷嗷"); } }; */ IA tiger = new IA() { @Override public void cry() { System.out.println("嗷嗷嗷嗷"); } }; tiger.cry(); } } interface IA { public void cry(); }
现在我们来看看它在底层中具体的名字:
System.out.println(tiger.getClass());我们用getClass()方法来获取名字。
它的名字就是: class 包名.外部类名$1
匿名内部类的使用细节:
1.匿名内部类既是一个类的定义,又是一个对象;从语法上看它既有类的特性又有对象的特征。
2.可以直接访问外部类的所有成员,包括私有的
3. 不可以添加修饰符,因为他就是个局部变量
4. 作用域仅仅在定义它的方法或代码块中
5.外部类不可以访问匿名内部类(因为他是个局部变量)
6.如果外部类和局部内部类成员发生重名时,默认遵守就近原则,如果向想访问外部类的成员,可以使用(外部类名.this.成员名)
实例内部类
在外部类中,内部类定义位置与外部类成员所处的位置相同,因此称为成员内部类
即未被static修饰的成员内部类
说明:
1. 可以访问外部类的所有成员,包括私有的。
例:
public class Main { public static void main(String[] args) { Outer outer = new Outer(); outer.T(); } } class Outer { private int n1 = 10; class Inner { public void say() { System.out.println("Outer 中的 n1 = " + n1); } } public void T(){ Inner inner = new Inner(); inner.say(); } }
只能通过这种方式访问。
2. 实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束
3. 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:【外部类名称.this.同名成员 来访问】
4. 实例内部类对象必须在先有外部类对象前提下才能创建
5. 实例内部类的非静态方法中包含了一个指向外部类对象的引用
6. 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。
静态内部类
被static修饰的内部成员类称为静态内部类
public class Main { public static void main(String[] args) { // 静态内部类对象创建 & 成员访问 OutClass.InnerClass innerClass = new OutClass.InnerClass(); innerClass.methodInner(); } } class OutClass { private int a; static int b; public void methodA() { a = 10; System.out.println(a); } public static void methodB() { System.out.println(b); } // 静态内部类:被static修饰的成员内部类 static class InnerClass { public void methodInner() { // 在内部类中只能访问外部类的静态成员 // a = 100; // 编译失败,因为a不是类成员变量 b = 200; // methodA(); // 编译失败,因为methodB()不是类成员方法 methodB(); } } }
【注意事项】
1. 在静态内部类中只能访问外部类中的静态成员
2. 创建静态内部类对象时,不需要先创建外部类对象
3.作用域:同其他成员,为整个类体
4.如果外部类和局部内部类成员发生重名时,默认遵守就近原则,如果向想访问外部类的成员,可以使用(外部类名.成员名)