内部类

内部类是在类体里面定义的一个类,分别有成员内部类、局部内部类、匿名内部类、静态内部类。

成员内部类

成员内部类作为外部类的一个成员存在,与外部类的属性和方法·并列,注意成员内部类不能定义static修饰的变量及方法,在内部类中可以随意使用外部类的成员方法和成员变量(即使是被private修饰的)。
image.png
除了上述方法可以调用内部类的成员变量和方法之外,也可以在主方法中创建内部类的对象直接调用:
image.png
使用this关键字获取内部类与外部类的引用:
image.png

局部内部类

image.png

匿名内部类

image.png
image.png
第一张图是匿名内部类,和第二张图是等价的。

静态内部类

image.png
静态内部类中只能调用静态的变量和方法,但是静态内部类中有一个很特殊的特点就是它可以创建main方法,这一特性可以用来调试程序。

内部类的继承

语法如下:
image.png

final关键字


在Java中,final关键字可以用来修饰类、方法和变量。

用final修饰类

当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类永远不会让它被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。

用final修饰方法

使用final方法的原因有两个:第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率,在早期的Java实现版本中,会将final方法转为内嵌调用,但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。因此,如果只有在想明确禁止该方法在子类中被覆盖的情况下才将方法设置为final的。注意类的private方法会被隐式地指定为final方法。
image.png
image.png

用final修饰变量

final关键字修饰常量是用的最多的,用final关键字定义的变量必须在声明时对其进行赋值操作,被final修饰的常量定义时需要使用大写字母命名,中间可以使用下划线连接,final修饰的变量在本次程序运行中的值是不可以被改变的。final除了可以修饰基本数据类型的常量,还可以修饰对象引用(由于数组也可以看作一个对象,所以final可以修饰数组),一旦一个对象引用被修饰为final后,它只能恒定指向一个对象,无法将其改变以指向另一个对象(向上或者向下转型)。但由于一个对象本身的值是可以改变的,因此为了使一个常量真正做到不可更改,可以将常量声明为static final,如下面的代码:
image.png

final关键字的注意点

final变量和普通变量的区别

先看一下下面这段代码的运行结果:

  1. /**
  2. * @author wjh
  3. * @date 2021/7/13 17:15
  4. * @Package PACKAGE_NAME
  5. */
  6. public class MyTest {
  7. public static void main(String[] args) {
  8. final String a = "hello";
  9. String b = "hello";
  10. String c = a + 2;
  11. String d = b + 2;
  12. String e = "hello2";
  13. System.out.println(c == e);
  14. System.out.println(d == e);
  15. }
  16. }

运行结果:
image.png
解释:当final变量是基本数据类型以及String类型时,如果在编译期间能知道它的确切值,则编译器会把它当做编译期常量使用。也就是说在用到该final变量的地方,相当于直接访问的这个常量,不需要在运行时确定。因此在上面的一段代码中,由于变量b被final修饰,因此会被当做编译器常量,所以在使用到b的地方会直接将变量b 替换为它的值,而对于变量d的访问却需要在运行时通过链接来进行。但是只有在编译期间能确切知道final变量值的情况下,编译器才会进行这样的优化,比如下面的这段代码就不会进行优化:

  1. public class Test {
  2. public static void main(String[] args) {
  3. String a = "hello2";
  4. final String b = getHello();
  5. String c = b + 2;
  6. System.out.println((a == c));
  7. }
  8. public static String getHello() {
  9. return "hello";
  10. }
  11. }

被final修饰的引用变量指向的对象内容可改变

我们知道被final修饰变量一旦初始化赋值之后就不能指向其它的对象,但这并不是说明被final修饰的对象的内容不能改变,仅仅只是不能再指向其它对象了,不能向上或者向上转型。

final和static的区别

final:
final可以修饰属性,方法,类,局部变量(方法中的变量)
final修饰的属性的初始化可以在编译期,也可以在运行期,初始化后不能被改变。
final修饰的属性跟具体对象有关,在运行期初始化的final属性,不同对象可以有不同的值。
final修饰的属性表明是一个常数(创建后不能被修改)。
final修饰的方法表示该方法在子类中不能被重写。
final修饰的类表示该类不能被继承。
对于基本类型数据,final会将值变为一个常数(创建后不能被修改);但是对于对象句柄(亦可称作引用或者指针),final会将句柄变为一个常数(进行声明时,必须将句柄初始化到一个具体的对象。而且不能再将句柄指向另一个对象。但是,对象的本身是可以修改的。这一限制也适用于数组,数组也属于对象,数组本身也是可以修改的。方法参数中的final句柄,意味着在该方法内部,我们不能改变参数句柄指向的实际东西,也就是说在方法内部不能给形参句柄再另外赋值)。

static:
static可以修饰属性,方法,代码段,内部类(静态内部类或嵌套内部类)。
static修饰的属性的初始化在编译期(类加载的时候),初始化后能改变。
static修饰的属性所有对象都只有一个值。
static修饰的属性强调它们只有一个。
static修饰的属性、方法、代码段跟该类的具体对象无关,不创建对象也能调用static修饰的属性、方法等。
static和“this、super”势不两立,static跟具体对象无关,而this、super正好跟具体对象有关。
static不可以修饰局部变量。

  1. /**
  2. * @author wjh
  3. * @date 2021/7/13 17:15
  4. * @Package PACKAGE_NAME
  5. */
  6. public class MyTest {
  7. public static void main(String[] args) {
  8. MyClass myClass1 = new MyClass();
  9. MyClass myClass2 = new MyClass();
  10. System.out.println("myClass1.d1(final修饰):" + myClass1.d1);
  11. System.out.println("myClass1.d2(static修饰):" + myClass1.d2);
  12. System.out.println("myClass2.d1(final修饰):" + myClass2.d1);
  13. System.out.println("myClass2.d2(static修饰):" + myClass2.d2);
  14. }
  15. }
  16. class MyClass {
  17. public final double d1 = Math.random();
  18. public static double d2 = Math.random();
  19. }

两次运行结果的比较:
image.pngimage.png
static final和final static:
两者没有区别,只不过是写的顺序不一样而已,static修饰的属性强调它们只有一个,final修饰的属性表明是一个常数(创建后不能被修改)。static final修饰的属性表示一旦给值,就不可修改,并且可以通过类名访问。static final也可以修饰方法,表示该方法不能重写,可以在不new对象的情况下调用。