Java 四大内部类总结——局部内部类,匿名内部类,成员内部类,静态内部类

1. 内部类

1.1 内部类概念

一个类的内部又完整的嵌套了另一个类结构。
被嵌套的类称为内部类(inner class)
嵌套其他类的类称为外部类(outer class)
特点:内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。

1.2 内部类语法

class Outer{ // 外部类
    class Inner{ // 内部类
        
    }
}

class Other{ // 其他类
    
}

1.3 四种内部类

  • 定义在外部类的局部位置上(比如方法/代码块内)

    • 局部内部类(有类名)

    • 匿名内部类(没有类名)🌟

  • 定义在外部类的成员位置上(本质就是一个成员)

    • 成员内部类(没用static修饰)

    • 静态内部类(使用static修饰)

(1) 局部内部类:定义在外部类的局部位置上(通常在方法/代码块内),且[有]类名

👿​(1)局部内部类定义在方法中/代码块 (2) 作用域在方法体或者代码块中 (3)本质仍然是一个类!!!

  1. 可以直接访问外部类的所有成员,包含私有的

  2. 不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。
    但是可以使用final 修饰,因为局部变量也可以使用final.(final就可以让类不可以被继承)

  3. 作用域:仅仅在定义它的方法或代码块中

  4. 局部内部类—访问---->外部类的成员【访问方式:直接访问】

    外部类—访问---->局部内部类的成员【访问方式:创建对象,再访问(注意:必须在作用域内)】

    外部其他类—不能访问----->局部内部类【因为 局部内部类地位是一个局部变量】

  5. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,
    如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问 :
    Outer02.this本质就是外部类的对象,于是就相当于用对象.n1去访问类的属性,就是 Outer02.this.n1

    public class LocalInnerClass {//
        public static void main(String[] args) {
            //演示一遍
            Outer02 outer02 = new Outer02();
            outer02.m1();
            System.out.println("outer02 的 hashcode=" + outer02); 
            // 对象outer02就是Outer02.this
        }
    }
    
    class Outer02 {  //外部类
        private int n1 = 100;
        private void m2() { //私有方法
            System.out.println("Outer02 m2()");
        }
        public void m1() {  //方法
            //1.局部内部类是定义在外部类的局部位置,通常在方法
            //3.不能添加访问修饰符,但是可以使用final 修饰
            //4.作用域 : 仅仅在定义它的方法或代码块中
            final class Inner02 {  //局部内部类(本质仍然是一个类)
                //2.可以直接访问外部类的所有成员,包含私有的
                private int n1 = 800;
                public void f1() {
    //5. 局部内部类可以直接访问外部类的成员,比如下面可以直接访问:外部类n1 和 m2()
    //7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,
    //   如果想访问外部类的成员,使用 外部类名.this.成员 去访问
    // Outer02.this 本质就是外部类的对象, 即哪个对象调用了m1,Outer02.this就是哪个对象,
    // 于是就相当于对象.n1去访问类的属性,就是 Outer02.this.n1
                    System.out.println("n1=" + n1 + " 外部类的 n1=" + Outer02.this.n1);
                    System.out.println("Outer02.this hashcode=" + Outer02.this);
                    m2();
                }
            }
            //6. 外部类访问内部类:外部类在方法中,可以创建Inner02对象,然后调用方法即可
            Inner02 inner02 = new Inner02();
            inner02.f1();
        }
    }
    
    class Outer01{}// 外部其他类 不能访问 局部内部类
    

(2) 匿名内部类:定义在外部类的局部位置上(通常在方法/代码块内),且[没有]类名

👿(1)定义在方法中 / 代码块;
(2) 作用域在 方法体 / 代码块 中;
(3)本质仍然是一个类;
(4)该类没有名字(但其实在系统底层分配有名字);
(5)同时还是一个对象;
匿名内部类—访问---->外部类的成员【访问方式:直接访问,包含私有的】
如果外部类和匿名内部类的成员重名时,默认遵循就近原则,
如果此时匿名内部类想访问外部类的成员,则可以使用(外部类名.this.成员)去访问 :
Outer02.this本质就是外部类的对象,于是就相当于用对象.n1去访问类的属性,就是 Outer02.this.n1

​ 外部类—访问---->匿名内部类的成员【访问方式:创建对象,再访问(注意:必须在作用域内)】
​ 外部其他类—不能访问----->匿名内部类【因为 匿名内部类地位是一个局部变量】
​ 不能添加访问修饰符,因为其本身相当于一个局部变量

  1. 基本语法:

    因为众所周知 抽象类和接口无法创建对象,所以这里面的new,其实是new的他们的实现类(子类 )对象,类名由系统分配

    匿名内部类大括号内其本质上就是一个实现类(子类),new就会立即创建出一个实现类(子类)对象

new 抽象类名/接口名(参数列表){
	重写抽象类或者接口中的抽象方法
};
  1. 基于接口的匿名内部类
    package com.hspedu.innerclass;
    /*
    接口匿名内部类的使用
     */
    public class AnonymousInnerClass {
        public static void main(String[] args) {
            Outer outer = new Outer();
            outer.method();
        }
    }
    
    class Outer{ // 外部类
        private int n1 = 10;  // 外部类属性
        public void method(){ // 外部类方法
            // 【基于接口的匿名内部类】 需求:想使用IA接口并创建对象,传统方式就是使用一个类实现该接口并创建对象
            //    Tiger tiger = new tiger();
            //    tiger.cry();
            // 但是Tiger类只用一次,后面不再使用了,每次想要实现IA接口就定义一个类很繁琐
            
            // 于是可以使用【匿名内部类】简化开发(既不创建Tiger类 又可以让老虎叫)
              // tiger的编译类型是:IA
              // tiger的运行类型是:匿名内部类XXX  => Outer$1
                /*
                  底层会分配一个类名 XXX  => Outer$1
                    class XXX implements IA{
                        @Override
                        public void cry() {
                            System.out.println("老虎叫唤aowuaowu");
                        }
                    }
                 */
              // new 出来的就是接口的实现类对象 即:class XXX implements IA
              // jdk底层创建匿名内部类Outer$1后,立马就创建了Outer$1的对象,把地址返回给tiger(多态:接口类型的变量tiger 可以指向 实现了IA接口类的对象实例)
              // 匿名内部类创建完对象后,这个匿名内部类就没有了,但对象还在,可以反复使用。
            IA tiger = new IA(){ // new了就是一个实现类对象
                @Override // 大括号内本质上就是一个实现类
                public void cry() {
                    System.out.println("老虎叫唤aowuaowu");
                }
            };
            System.out.println("tiger 的运行类型" + tiger.getClass());  // 这里使用getClass获取对象 tiger 的运行类型 Outer$1
            tiger.cry();
    
    
        }
    }
    
    interface IA{ // 接口
        public void cry();
    }
    
    //class Tiger implements  IA{  // 繁琐
    //    @Override
    //    public void cry() {
    //        System.out.println("老虎叫唤aowuaowu");
    //    }
    //}
    
    
  2. 基于类的匿名内部类
package com.hspedu.innerclass;
/*
匿名内部类的使用
 */
public class AnonymousInnerClass {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.method();
    }
}

class Outer{ // 外部类
    private int n1 = 10;  // 外部类属性
    public void method(){ // 外部类方法

        // 【基于类的匿名内部类】 需求:想使用Father类并创建对象,传统方式就是使用这个类并创建对象↓
        // Father father = new Father("爹名");
        
    // 这里打了大括号{},那么{}中就相当于是抽象类的实现类(或者就说子类),new了就是new出来一个实现类(子类)对象,之后用类的引用指向这个实现类(子类)
        //   father的编译类型是:Father
        //   father的运行类型是:匿名内部类XXX  => Outer$2 按顺序编号
        //   底层会创建匿名内部类 class Outer$2 extends Father(){},并且直接返回了匿名内部类Outer$2的对象
        //   参数列表默认会传递给类的构造器
        Father father = new Father("爹名"){
            @Override
            public void test() {
                System.out.println("匿名内部类重写了test方法,要输出一个" + name);
            }
        };
        System.out.println("father 的运行类型" + father.getClass()); // 这里使用getClass获取对象 father 的运行类型 Outer$2
        father.test();

    }
}



abstract class Father{   // 类
    public String name;

    public Father(String name) {  // 类的构造器
        this.name = name;
    }

    public abstract void test();// 抽象方法
}

  1. 一些其他解释
    • ​ 匿名内部类—访问---->外部类的成员【访问方式:直接访问,包含私有的】

      ​ 如果外部类和匿名内部类的成员重名时,默认遵循就近原则,

      ​ 如果此时匿名内部类想访问外部类的成员,则可以使用(外部类名.this.成员)去访问 :

      Outer05.this本质就是外部类的对象,于是就相当于用对象.n1去访问类的属性,就是 Outer05.this.n1

    • ​ 外部类—访问---->匿名内部类的成员【访问方式:创建对象,再访问(注意:必须在作用域内)】

    • ​ 外部其他类—不能访问----->匿名内部类【因为 匿名内部类地位是一个局部变量】

    • ​ 不能添加访问修饰符,因为其本身相当于一个局部变量

    public static void main(String[] args) {
     	Outer05 outer05 = new Outer05();
     	outer05.f1();
    	System.out.println("main outer05 hashcode=" + outer05);
     	}
    }
    
    class Outer05 {
    	private int n1 = 99;
    	public void f1() {
        //创建一个基于类的匿名内部类
        //不能添加访问修饰符,因为它的地位就是一个局部变量
        //作用域 : 仅仅在定义它的方法或代码块中
    		Person p = new Person(){
     			private int n1 = 88;
     			@Override
     			public void hi() {
                    //可以直接访问外部类的所有成员,包含私有的
                    //如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,
                    //默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
                    System.out.println("匿名内部类重写了 hi 方法 n1=" +n1 + "外部内的n1=" + Outer05.this.n1 );    //和主函数中打印的一样
                    //Outer05.this 就是调用 f1 的对象,即:谁调用的f1 这个Outer05.this 就是谁————即为主函数中的outer05
                    System.out.println("Outer05.this hashcode=" + Outer05.this) //>>>>> 会输出:调用 f1 的对象的hashcode
    			}
     		};
     
            p.hi();//调用时候:动态绑定, 运行类型是 Outer05$1         // >>>>> 会输出:匿名内部类重写了 hi 方法 n1=88  外部内的n1=99  
        }
        
      """''''''''''''''' 下面的这模块代码是和上面的代码或者关系存在演示的     
     		//调用时候:也可以直接调用, 匿名内部类本身也是返回对象
             new Person(){
             	@Override
             	public void hi() {
             		System.out.println("匿名内部类重写了 hi 方法,哈哈...");//>输出匿名内部类重写了 hi 方法,哈哈...【不输出Person hi()】
             	}
             	@Override
             	public void ok(String str) {
             		super.ok(str);
             	}
             }.ok("jack");   											// >>>>> 会输出Person ok()jack
      """''''''''''''''''''''       
    
    class Person { //类
    	public void hi() {
     		System.out.println("Person hi()");
     	}
     	public void ok(String str) {
            System.out.println("Person ok()" + str);
        }
    }
    
    1. 匿名内部类使用场景
      【传统的方法】
      public class AnonymousInnerClass01 {
          public static void main(String[] args) {
              A a = new A(); // 创建实现类的对象
              f1(a);
          }
          public static void f1(IL il){
              il.show();
          }
      }
      
      interface IL{
          void show();
      }
      
      class A implements IL{  //类实现IL接口,硬编码方式
          @Override
          public void show() {
              System.out.println("showwwww");
          }
      }
          
          
      【匿名内部类的使用】
      public class AnonymousInnerClass01 {
          public static void main(String[] args) {
              f1(new IL() {  // 大括号里面就相当于是接口的实现类,给f1传入的就是接口的实现类对象 !!!!
                  @Override
                  public void show() {
                      System.out.println("showwwww");
                  }
              });
      
          }
          public static void f1(IL il){  // 需要传入接口的实现类对象
              il.show();
          }
      }
      
      interface IL{
          void show();
      }
      
      

(3) 成员内部类:定义在外部类的成员位置上(属性 方法这些地方),且[没有]static修饰

  • 成员内部类—访问---->外部类的成员【访问方式:直接访问,包含私有的】
如果外部类和内部类的成员重名时,默认遵循就近原则,如果此时内部类想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问 
  • 外部类—访问---->成员内部类的成员【访问方式:创建成员内部类的对象,再访问(注意:必须在作用域内)】
  • 外部其他类—访问----->成员内部类【两种方法】【外部类对象.new 内部类名()】
  • 可以添加任意的访问修饰符(public protected 默认 private),因为其本身相当于一个成员,其作用域范围也和外部类的其他成员一样
public class MemberInnerClass01 {
    public static void main(String[] args) {
        Outer08 outer08 = new Outer08();
        outer08.t1();

        // 方式一 外部其他类调用成员内部类的方式 
        Outer08.Inner inner = outer08.new Inner();  // 用外部类的对象new内部类Inner
        inner.say();

        // 方式二 在外部类中写一个编写一个方法可以返回一个Inner08 的对象实例
        Outer08.Inner innerinstance = outer08.getInnerInstance();
        innerinstance.say();

    }
}

class Outer08{ // 外部类
    private int n1 = 10;
    public String name = "张三";

     public class Inner{  // 成员内部类
        private int n1 = 66;
        public void say(){
            // 成员内部类可以访问外部类的所有成员,包括私有的
            System.out.println("Outer08的name=" + name);
            System.out.println("内部类Inner 的n1=" + n1);
            System.out.println("外部类Outer08 的n1=" + Outer08.this.n1);  // 重名的时候,使用外部类名.this访问外部类属性
        }
    }
    
    public Inner getInnerInstance(){  // 方法二 ,返回一个Inner 对象
        return new Inner();
    }

    // 再写一个方法 外部类访问成员内部类 创建对象访问
    public void t1(){
        Inner inner = new Inner();
        inner.say();
    }
}

(4) 静态内部类:定义在外部类的成员位置上(通常在方法/代码块),且[有]static修饰

  • 静态内部类—访问---->外部类的成员【访问方式:直接访问,包含私有的】【🔴但是只能访问静态成员!!!!】
如果外部类和内部类成员重名时,默认遵循就近,如果此时内部类想访问外部类成员,可以使用 (外部类名.成员)去访问 【因为成员是静态的,类名调用,就不用加this了】
  • 外部类—访问---->静态内部类的成员【访问方式:创建内部类的对象,再访问(注意:必须在作用域内)】
  • 外部其他类—访问----->成员内部类【因为静态内部类是 可以通过类名直接访问(前提满足访问权限),所以new一个Outer08.Inner()对象,在调用方法】
  • 可以添加任意的访问修饰符(public protected 默认 private),因为其本身相当于一个成员,其作用域范围也和外部类的其他成员一样。
public class MemberInnerClass01 {
    public static void main(String[] args) {
        Outer08 outer08 = new Outer08();
        outer08.m1();

        // 外部其他类使用静态内部类  因为静态内部类是 可以通过类名直接访问(前提满足访问权限),所以new一个Outer08.Inner()对象,在调用方法
        new Outer08.Inner().say();
    }
}

class Outer08{ // 外部类
    private int n1 = 10;
    public String name = "张三";
    private static int n2 = 99;

    public static class Inner{  // 静态内部类 有static修饰
        private int n2 = 1;
        public void say(){
            System.out.println("外部类的静态属性n2=" + Outer08.n2);  // 重名访问外部类的属性 直接通过外部类名.成员访问
            System.out.println("静态内部类的属性n2 = " +n2); 		// 因为外部类的n1和name都是非静态的,所以不能访问
        }
    }

    public void m1(){  // 外部类访问静态内部类的成员:创建对象在访问
        new Inner().say();
    }
}
  • 17
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,参数传递有两种方式:值传递和引用传递。值传递是指将实际参数的值复制一份给形式参数,而引用传递是指将实际参数的引用(地址)复制一份给形式参数。 Java中的基本数据类型(如int、float等)都是采用值传递的方式进行参数传递。这意味着在方法内部修改形式参数的值不会影响到实际参数的值。 而对于对象类型,Java采用的是引用传递。当将一个对象作为参数传递给方法时,实际上传递的是对象的引用(地址),而不是对象本身。这意味着在方法内部修改对象的属性值会影响到实际对象的属性值。 需要注意的是,虽然对象的引用被传递给了方法,但是如果在方法内部将引用指向了一个新的对象,那么这个改变不会影响到原始对象。 下面是一个示例代码来说明Java中的引用传递: ```java public class Main { public static void main(String[] args) { Person person = new Person("Alice"); System.out.println("Before changeName method: " + person.getName()); changeName(person); System.out.println("After changeName method: " + person.getName()); } public static void changeName(Person p) { p.setName("Bob"); } } class Person { private String name; public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ``` 输出结果为: ``` Before changeName method: Alice After changeName method: Bob ``` 在上述代码中,通过引用传递将`person`对象传递给`changeName`方法,方法内部修改了对象的属性值,导致在方法外部也能看到修改后的结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值