java内部类

223 阅读6分钟

内部类

概念

1645925822806.png 所谓内部类就是在一个类的内部再申明一个类。

凡是申明在类的成员位置的就是成员内部类,凡是申明在方法或者代码块中的类都是局部内部类。

java中最繁琐的机制之一。

成员内部类

非静态成员内部类

基本写法

案例:

 /**
  * @author 戴着假发的程序
  */
 public class Outer1 {
     private String name;
     public void showName(){
         System.out.println(name);
     }
     // 非静态成员内部类  可以使用任何访问修饰符
     public class Inner{
         private String innerName;
         public void setInnerName(String innerName){
             this.innerName = innerName;
         }
         public void showInnerName(){
             System.out.println("innerName:"+innerName);
         }
         // 在非静态成员的内部类中不能申明静态的方法和静态的成员变量
        //public static int age = 100;
         //public static void showInnerClass(){
         //    System.out.println(".....");
         //}
     }
 }

非静态成员内部类的对象创建。

如果要创建内部类的对象,则必须先有外部类对象。

 /**
  * @author 戴着假发的程序
  */
 public class InnerTest {
     public static void main(String[] args) {
         // 要创建非静态成员内部类的对象时,要先创建外部内对象
         Outer1 outer = new Outer1();
         // 使用外部类对象.new 创建内部类对象
         Outer1.Inner inner = outer.new Inner();
         inner.setInnerName("内部类的名字");
         inner.showInnerName();
     }
 }

tips:

在非静态内部类中不能申明静态方法。 在非静态的内部类中不能申明静态的成员变量,但是可以申明常量。

在外部类的非静态方法中可以直接创建内部非静态成员类:

image-20210922085700060.png 内部类的一些特征

编译:所有的成员内部类都会被编译成一个独立的字节码文件,文件的名称为“外部类的类名$内部类类名.class”

image-20210922085524501.png 访问权限问题

成员的内部类可以使用任何访问修饰符,表现的状态和其他成员一样。

在内部类中可以访问外部类的所有成员 在非静态成员内部类中可以调用所有的外部类方法 在非静态成员内部类中使用外部类的this的时候,语法是:外部类类名.this。

image.png

静态成员内部类

案例:

 /**
  * @author 戴着假发的程序
  */
 public class Outer2 {
     private static int age = 18;
     private String name = "外部内的名字";
     private void setName(String name){
         this.name = name;
     }
     // 静态成员内部类
     public static class Inner{
         private String innerName;
         public void setInnerName(String innerName){
             this.innerName = innerName;
         }
         public void method(){
             //调用外部内成员
             // 静态成员可以调用
             System.out.println(age);
             //非静态成员无法调用
             //System.out.println(name);
         }
         // 可以写静态方法
         public static  void showClassName(){
             System.out.println(Inner.class.getName());
             //调用外部内成员
             // 静态成员可以调用
             System.out.println(age);
             //非静态成员无法调用
             //System.out.println(name);
         }
     }
 }
 /**
  * @author 戴着假发的程序
  */
 public class InnerTest {
     public static void main(String[] args) {
         //创建静态内部类对象,不需要创建外部类对象
         Outer2.Inner inner = new Outer2.Inner();
         inner.method();
         inner.setInnerName("哈哈哈");
         // 调用内部类的静态方法
         Outer2.Inner.showClassName();
     }
 }

静态内部类中可以申明静态和非静态的方法和成员变量。

静态内部类中可以调用外部类的任何静态的成员变量和方法,但是不能调用任何非静态的方法和变量,更不能使用外部类的this对象。

image.png

局部内部类

申明在类的代码块中的类就是局部内部类。 就是和局部变量在一个作用域中的类。

案例:

 /**
  * @author 戴着假发的程序
  */
 public class Outer {
     public void method(){
         System.out.println("execute method");
         // 申明一个局部内部类
         // 局部内部类不能使用任何访问修饰符和static修饰符
         class Inner{
                 public String innerName = "局部内部类";
                 // 不能申明静态的成员变量和方法和内部类
                 //public static int age = 18;
                 public void showInnerName(){
                     System.out.println(innerName);
                 }
         }
         //局部内部类只能在当前所在的局部作用域中使用
         // 创建局部内部类对象
         Inner inner = new Inner();
         inner.showInnerName();
     }
 }

局部内部类的使用范围就是局部范围,就是申明的范围内。 局部内部类无法使用任何访问修饰符修饰,也不能修饰为static

局部内部类访问外部类中的成员或者局部变量。

在局部内部类中可以访问外部了的任何成员。可以使用外部类的this对象等等。 在局部内部类中如果要使用外部类的局部变量,如果jdk是1.7之前,则局部变量必须是final修饰的,1.7之后就不需要了,其实就是默认就是final。

image.png

局部内部类在方法执行结束之后,内对象依然存在的情况:

 public class A {
     public void show() {
         
     }
 }
 /**
  * @author 戴着假发的程序员
  * @TODO 
  * @organization 飞虎队
  * 2020年8月31日 上午10:19:08
  */
 public class Outter {
     public A getA() {
         int x = 10;
         class B extends A{
             public void show() {
                 System.out.println(x);
             }
         }
         B b = new B();
         return b;
     }
     
     public static void main(String[] args) {
         A a = new Outter().getA();
         System.out.println(a);
         a.show();
     }
 }   

局部内部类的编译情况:

源代码:

 /**
  * @author 戴着假发的程序员
  * @TODO 
  * @organization 飞虎队
  * 2020年8月31日 上午10:25:00
  */
 public class Outter {
     public void methoda() {
         class Inner{}
     }
     public void methodb() {
         class Inner{}
     }
 }

编译后的情况:

image.png 局部内部类编译后的类名是:外部类类名$index内部类类名.class 。 这里的index是编译器根据方法的编译前后顺序给的索引。

抽象内部类和内部接口

[1]内部类能不能申明为抽象类?

成员非静态内部类可以是抽象类,但是只能被内部类继承

image.png [2]成员静态内部类可以被申明为抽象类.

 /**
  * @author 戴着假发的程序员
  * @TODO 
  * @organization 飞虎队
  * 2020年8月31日 上午10:34:10
  */
 public class Outter {
     public abstract static class Inner{
     }
     //继承内部抽象类
     class B extends Inner{
     }
 }
 //外部的其他类可以继承抽象静态内部类
 class A extends Outter.Inner{
 }

[3]局部内部类,局部内部类可以申明为抽象的。

 /**
  * @author 戴着假发的程序员
  * @TODO 
  * @organization 飞虎队
  * 2020年8月31日 上午10:34:10
  */
 public class Outter {
     
     public void method() {
         abstract class Inner{
             
         }
         //继承局部的抽象内部类
         class A extends Inner{
             
         }
     }
 }

[4]接口内部申明内部接口

接口中可以申明:内部接口,成员内部类,局部内部类。

在任何一个类中,也可以申明内部接口。

内部接口可以在外部直接实现。

 /**
  * @author 戴着假发的程序员
  * @TODO 
  * @organization 飞虎队
  * 2020年8月31日 上午10:42:06
  */
 public interface IOutter {
     //内部接口
     public interface IInner{
         
     }
     //内部类
     public class AInner {
         
     }
     //静态成员内部类
     public static class SAInner{
         
     }
     default void method() {
         //局部内部类
         class Inner{
             
         }
     }
 }

测试:

 /**
  * @author 戴着假发的程序员
  * @TODO 
  * @organization 飞虎队
  * 2020年8月31日 上午10:47:37
  */
 public class Test {
     public static void main(String[] args) {
         //创建接口中的非静态成员内部类对象
         IOutter outter = new St();
         IOutter.AInner ainner = new AInner();
         //创建接口的静态成员内部类对象
         IOutter.SAInner sainner = new SAInner();
         //创建内部接口对象
         IOutter.IInner iinner = new Geek();
     }
 }
 //实现外部接口
 class St implements IOutter{
 }
 //实现内部接口
 class Geek implements IOutter.IInner{
 }

接口中的静态方法:jdk8的新特性

匿名内部类

所谓匿名内部类就和匿名对象一样,没有名字。

常用来直接继承抽象类,或者实现接口。

匿名内部类只能使用一次,不能使用第二次。

案例:使用匿名内部类实现一个线程。

 /**
  * @author 戴着假发的程序员
  * @TODO 匿名内部类
  * @organization 飞虎队 2020年8月31日 上午11:07:06
  */
 public class Main {
     public static void main(String[] args) {
         new Main().mt();
     }
 ​
     public void mt() {
         // 开启一个线程 线程类:继承Thread,实现Runnable接口
         // 使用匿名内部类继承Thread类
         new Thread() {
             public void run() {
                 System.out.println("匿名内部类继承Thread类实现一个线程类");
                 method();
             }
 ​
             public void method() {
                 System.out.println("匿名内部类自定义的方法");
             }
         }.start();
         // 使用匿名内部类实现Runnable接口
         new Thread(new Runnable() {
             private String name = "runnable";
 ​
             public void run() {
                 System.out.println("匿名内部类实现Runnable接口实现一个线程类");
                 System.out.println(name);
                 m();//调用外部类的其他成员
             }
         }).start();
     }
 ​
     public void m() {
         System.out.println("外部类的成员方法");
     }
 }

匿名内部类就是使用{}来实现或者继承某一个类。

常见的方式:

 类名/接口  对象名 = new 类名/接口(){实现接口或者继承父类的重写的方法}
         //匿名内部类继承Thread
         Thread ta = new Thread() {
             public void run() {
                 System.out.println("一个线程");
             } 
         };
         ta.start();

使用场景:

当一个接口或者一个父类申明的抽象方法非常有限,而我们只需要这个类对象一次,这时就使用匿名内部类创建对象。

         // 使用匿名内部类实现Runnable接口
         new Thread(new Runnable() {
             private String name = "runnable";
             public void run() {
                 System.out.println("匿名内部类实现Runnable接口实现一个线程类");
                 System.out.println(name);
                 m();//调用外部类的其他成员
             }
         }).start();

匿名内部类中同样可以使用外部类的任何成员。

匿名内部类谈不上任何修饰符。

匿名内部类的编译:

匿名内部类也会被独立的编译成一个字节码文件,命名为 ”外部类类名$index.class”。 index就是编译器给所有的内部类给的索引。

image.png

静态内部类的单利中的使用

传统的方式:

 /**
  * @author 戴着假发的程序
  */
 public class Single {
     private static Single instance = new Single();
     private Single(){
         System.out.println("创建Single对象");
     }
 ​
     public static void method(){}
 ​
     public synchronized static Single getInstance(){
 //        if(instance == null){
 //            instance = new Single();
 //        }
         return instance;
     }
 }
 /**
  * @author 戴着假发的程序员
  * @TODO 
  * @organization 飞虎队
  * 2020年8月31日 上午11:25:50
  */
 public class Singleton {
     //申明静态的私有的当前类对象成员
 //  private static Singleton instance;
     //使用静态内部类实例化对象
     private static class Inner{
         //在类加载的创建
         private static Singleton instance = new Singleton();
     }
     //构造方法私有化
     private Singleton() {
     }
     //静态public的获取当前类对象的方法
     public static Singleton getInstance() {
         //加载内部类
         return Inner.instance;
     }
     
     public static void main(String[] args) {
         Singleton s1 = Singleton.getInstance();
         Singleton s2 = Singleton.getInstance();
         System.out.println(s1 == s2);
     }
 }

测试:

 /**
  * @author 戴着假发的程序
  */
 public class SingleTest {
     public static void main(String[] args) {
         // 之前的模式(使用synchronized同步)
         // [1] 懒汉模式,对象一开始是没有创建的,第一次使用的时时候才创建。主要问题在于synchronized降低效率
         // [2] 饿汉模式,一开始就给创建了,但是如果创建之后很长时间都不适用,就是浪费内存
 //        Single single = null;
 //        //Single single = Single.getInstance();Single.getInstance();Single.getInstance();Single.getInstance();
 //        Single.method();
 ​
         Singleton singleton = null;
         // 调用其他的静态方法,
         Singleton.method();
 ​
         // 获取实例
         Singleton.getInstance();Singleton.getInstance();Singleton.getInstance();Singleton.getInstance();Singleton.getInstance();
     }
 }

戴着假发的程序员出品 抖音ID:戴着假发的程序员欢迎关注