【Java笔记】18 内部类

一、基本介绍

一个类的内部完整的嵌套了另一个类结构,被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是类的五大成员(属性、方法、构造器、代码块、内部类)之一

内部类最大的特点就是可以直接访问私有属性,并可以体现类与类之间的包含关系

  • 基本语法
    class Outer{ //外部类
    class Inner{ //内部类
    }
    }
    class Other{ //外部其他类
    }
  1. public class InnerClass01 { // 外部其他类
  2. public static void main(String[] args) {
  3. }
  4. }
  5. class Outer{ // 外部类
  6. private int n1 = 100; // 属性
  7. public Outer(int n1) { // 构造器
  8. this.n1 = n1;
  9. }
  10. public void m1(){ // 方法
  11. System.out.println("m1()");
  12. }
  13. { // 代码块
  14. System.out.println("代码块");
  15. }
  16. class Inner{ // 内部类
  17. }
  18. }

二、分类

  • 定义在外部类局部位置上(比如方法内):
    1. 局部内部类(有类名)
      • 可以直接访问外部类的所有成员,包含私有的
      • 不能添加访问修饰符,但可以用final修饰,加final修饰后,不能被继承
      • 作用域:定义它的方法或代码块中
      • 局部内部类可以直接访问外部类的成员
      • 外部类访问局部内部类的成员,创建对象,再访问
      • 外部其他类不能访问局部内部类(因为局部内部类是一个局部变量)
      • 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,可以使用 外部类名.this.成员
  1. /**
  2. * 局部内部类
  3. */
  4. public class LocalInnerClass { // 外部其他类
  5. public static void main(String[] args) {
  6. Outer02 outer02 = new Outer02();
  7. outer02.m1();
  8. }
  9. }
  10. class Outer02 { // 外部类
  11. private int n1 = 100;
  12. private void m2() {
  13. System.out.println("Outer02 m2()");
  14. } // 私有方法
  15. public void m1() { // 方法
  16. // 1. 局部内部类是定义在外部类的局部位置,通常在方法
  17. // 3. 不能添加访问修饰符,但可以用final修饰
  18. final class Inner02 { // 局部内部类
  19. // 2. 可以直接访问外部类的所有成员,包含私有的
  20. private int n1 = 900;
  21. public void f1(){
  22. // 局部内部类可以直接访问外部类的成员
  23. // 如果外部类和局部内部类的成员重名时,遵循就近原则,如果想访问外部类的成员,使用外部类名.this.成员
  24. // Outer02.this本质就是外部类的对象,即哪个对象调用了m1,Outer02.this就是哪个对象
  25. System.out.println("n1=" + n1 + " 外部类的n1=" + Outer02.this.n1);
  26. m2();
  27. }
  28. }
  29. // Inner02加final修饰,不能被继承
  30. // class Inner03 extends Inner02 {
  31. //
  32. // }
  33. // 外部类在方法中,可以创建Inner02对象,然后调用方法
  34. Inner02 inner02 = new Inner02();
  35. inner02.f1();
  36. }
  37. }
  1. 2.匿名内部类
  2. - 本质是类、内部类、没有名字,同时还是一个对象
  3. new 类或接口(参数列表){
  4. // 类体
  5. };
  1. - 基于接口的匿名内部类
  1. public class AnonymousInnerClass {
  2. public static void main(String[] args) {
  3. Outer04 outer04 = new Outer04();
  4. outer04.method();
  5. }
  6. }
  7. class Outer04{
  8. private int n1 = 10; // 属性
  9. public void method(){ // 方法
  10. // 基于接口的匿名内部类 使用IA接口并创建对象
  11. // 传统方式:写一个类,实现该接口,并创建对象
  12. // 类只使用一次,可以使用匿名内部类简化开发
  13. // tiger的编译类型是IA
  14. // tiger的运行类型是匿名内部类 Outer04$1
  15. // jdk底层在创建匿名内部类Outer04$1,立即创建了Outer04$1实例并把地址返回给了tiger
  16. /** 底层会分配类名 Outer04$1
  17. class Outer04$1 implements IA{
  18. @Override
  19. public void cry(){
  20. System.out.println("老虎叫..");
  21. }
  22. }
  23. */
  24. // 匿名内部类使用一次就不能再使用,但tiger作为一个对象还可以继续使用
  25. IA tiger = new IA() {
  26. @Override
  27. public void cry() {
  28. System.out.println("老虎叫..");
  29. }
  30. };
  31. System.out.println("tiger的运行类型=" + tiger.getClass()); // 输出 tiger的运行类型=class java18.innerclass.Outer04$1
  32. }
  33. }
  34. interface IA { // 接口
  35. public void cry();
  36. }
  1. - 基于类的匿名内部类
  1. public class AnonymousInnerClass {
  2. public static void main(String[] args) {
  3. Outer04 outer04 = new Outer04();
  4. outer04.method();
  5. }
  6. }
  7. class Outer04{
  8. private int n1 = 10; // 属性
  9. public void method(){ // 方法
  10. // 基于类的匿名内部类
  11. // father编译类型 Father
  12. // father运行类型 Outer04$1
  13. /** 底层会创建匿名内部类Outer04$1
  14. * class Outer04$1 extends Father{
  15. * @Override
  16. * public void test() {
  17. * System.out.println("匿名内部类重写了test方法");
  18. * }
  19. * }
  20. */
  21. // 参数列表会传递给构造器
  22. Father father = new Father("jack"){
  23. @Override
  24. public void test() {
  25. System.out.println("匿名内部类重写了test方法");
  26. }
  27. };
  28. System.out.println("father对象的运行类型="+father.getClass()); // father对象的运行类型=class java18.innerclass.Outer04$1
  29. father.test();
  30. }
  31. }
  32. class Father{ // 类
  33. public Father(String name) {
  34. System.out.println("接收到名字:"+name);
  35. }
  36. public void test(){ // 方法
  37. }
  38. }
  1. - 基于抽象类的匿名内部类
  1. public class AnonymousInnerClass {
  2. public static void main(String[] args) {
  3. Outer04 outer04 = new Outer04();
  4. outer04.method();
  5. }
  6. }
  7. class Outer04{
  8. private int n1 = 10; // 属性
  9. public void method(){ // 方法
  10. // 基于抽象类的匿名内部类
  11. Animal animal = new Animal() {
  12. @Override
  13. void eat() {
  14. System.out.println("小狗吃骨头..");
  15. }
  16. };
  17. animal.eat();
  18. }
  19. }
  20. abstract class Animal{ // 类
  21. abstract void eat();
  22. }
  1. 匿名内部类既是一个类的定义,同时本身也是一个对象,语法上既有定义类的特征也有创建对象的特征,可以调用匿名内部类方法
  2. 可以直接访问外部类的所有成员,包括私有的;外部其他类不能访问匿名内部类
  3. 不能添加访问修饰符
  4. 作用域:在定义它的方法或代码块中
  5. 外部类和匿名内部类的成员重名时,匿名内部类访问遵循就近原则,如果想访问外部类的成员,则可以使用 外部类名.this.成员
  1. public class AnonymousInnerClassDetail {
  2. public static void main(String[] args) {
  3. Outer05 outer05 = new Outer05();
  4. outer05.f1();
  5. //外部其他类---不能访问----->匿名内部类
  6. System.out.println("main outer05 hashcode=" + outer05);
  7. }
  8. }
  9. class Outer05 {
  10. private int n1 = 99;
  11. public void f1() {
  12. //创建一个基于类的匿名内部类
  13. //不能添加访问修饰符,因为它的地位就是一个局部变量
  14. //作用域 : 仅仅在定义它的方法或代码块中
  15. Person p = new Person(){
  16. private int n1 = 88;
  17. @Override
  18. public void hi() {
  19. //可以直接访问外部类的所有成员,包含私有的
  20. //如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,
  21. //默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
  22. System.out.println("匿名内部类重写了 hi方法 n1=" + n1 +
  23. " 外部内的n1=" + Outer05.this.n1 );
  24. //Outer05.this 就是调用 f1的 对象
  25. System.out.println("Outer05.this hashcode=" + Outer05.this);
  26. }
  27. };
  28. p.hi();//动态绑定, 运行类型是 Outer05$1
  29. //也可以直接调用, 匿名内部类本身也是返回对象
  30. // class 匿名内部类 extends Person {}
  31. // new Person(){
  32. // @Override
  33. // public void hi() {
  34. // System.out.println("匿名内部类重写了 hi方法,哈哈...");
  35. // }
  36. // @Override
  37. // public void ok(String str) {
  38. // super.ok(str);
  39. // }
  40. // }.ok("jack");
  41. }
  42. }
  43. class Person {//类
  44. public void hi() {
  45. System.out.println("Person hi()");
  46. }
  47. public void ok(String str) {
  48. System.out.println("Person ok() " + str);
  49. }
  50. }
  1. - 最佳实践 当参数传递
  1. public class InnerClassExercise01 {
  2. public static void main(String[] args) {
  3. //当做实参直接传递,简洁高效
  4. f1(new IL() {
  5. @Override
  6. public void show() {
  7. System.out.println("这是一副名画~~...");
  8. }
  9. });
  10. //传统方法
  11. f1(new Picture());
  12. }
  13. //静态方法,形参是接口类型
  14. public static void f1(IL il) {
  15. il.show();
  16. }
  17. }
  18. //接口
  19. interface IL {
  20. void show();
  21. }
  22. //类->实现IL => 编程领域 (硬编码)
  23. class Picture implements IL {
  24. @Override
  25. public void show() {
  26. System.out.println("这是一副名画XX...");
  27. }
  28. }
  • 定义在外部类的成员位置上:
    1. 成员内部类(没用static修饰)
      • 可以直接访问外部类的所有成员,包括私有的
        ```java public class MemberInnerClass01 { public static void main(String[] args) { Outer08 outer08 = new Outer08(); outer08.t(); } }

class Outer08 { private int n1 = 90; public String name = “kk”; // 可以添加任意访问修饰符 class Inner08 { // 成员内部类 public void say() { // 可以访问外部类的所有成员,包括私有 System.out.println(“n1=”+n1+” name=”+name); } } public void t(){ // 使用成员内部类 Inner08 inner08 = new Inner08(); inner08.say(); } }

  1. - 可以添加任意访问修饰符(publicprotected、默认、private),因为它是一个成员
  2. - 作用域:和外部类成员一样,为整个类体
  3. - 成员内部类访问外部类成员,直接访问
  4. - 外部类访问成员内部类,创建对象,再访问
  5. - 外部其他类访问成员内部类
  6. ```java
  7. public class MemberInnerClass01 {
  8. public static void main(String[] args) {
  9. Outer08 outer08 = new Outer08();
  10. outer08.t();
  11. // 外部其他类使用成员内部类的方式
  12. // 1. outer08.new Inner08(); 相当于把new Inner08()当作是outer08的成员
  13. Outer08.Inner08 inner08 = outer08.new Inner08();
  14. // 2. 在外部类中,写一个方法,返回Inner08对象
  15. Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
  16. }
  17. }
  18. class Outer08 {
  19. private int n1 = 90;
  20. public String name = "kk";
  21. class Inner08 { // 成员内部类
  22. public void say() {
  23. // 可以访问外部类的所有成员,包括私有
  24. System.out.println("n1="+n1+" name="+name);
  25. }
  26. }
  27. public void t(){
  28. // 使用成员内部类
  29. Inner08 inner08 = new Inner08();
  30. inner08.say();
  31. }
  32. public Inner08 getInner08Instance(){
  33. return new Inner08();
  34. }
  35. }
  1. - 外部类和内部类成员重名时,内部类访问默认遵循就近原则,如果想访问外部类的成员,则可以使用 外部类名.this.成员
  1. 静态内部类(使用static修饰)
    • 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
    • 可以添加任意访问修饰符 public,protected,默认,private
    • 作用域:整个类体
    • 静态内部类访问外部类 直接访问所有静态成员
    • 外部类访问静态内部类 创建对象再访问
    • 外部其他类使用静态内部类
    • 如果外部类和静态内部类的成员重名时,静态内部类访问的时,默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)
      ```java public class StaticInnerClass01 { public static void main(String[] args) { Outer10 outer10 = new Outer10(); outer10.m1(); // 外部其他类使用静态内部类 // 1. 静态内部类满足权限时可以通过类名直接访问 Outer10.Inner10 inner10 = new Outer10.Inner10(); inner10.say(); // 2. 编写一个方法,可以返回静态内部类对象实例 Outer10.Inner10 inner101 = outer10.getInner10(); inner101.say(); // 3. 静态方法 Outer10.Inner10 inner102 = Outer10.getInner10_(); inner102.say(); } }

class Outer10 { // 外部类 private int n1 = 10; private static String name = “张三”; // 可以添加任意访问修饰符 public,protected,默认,private // 作用域为整个类体 static class Inner10 { // 静态内部类 public void say() { // 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员 System.out.println(name); } } public void m1(){ // 外部类访问静态内部类 创建对象再访问 Inner10 inner10 = new Inner10(); inner10.say();

  1. }
  2. public Inner10 getInner10(){
  3. return new Inner10();
  4. }
  5. public static Inner10 getInner10_(){
  6. return new Inner10();
  7. }

} ```