首页 > 其他分享 >内部类

内部类

时间:2023-03-29 12:44:46浏览次数:20  
标签:内部 void System public println class

内部类【可以直接访问私有属性,并且可以体现类与类之间的包含关系】

内部 ---> 外部类 外部类 ---> 内部 外部其它类 ---> 内部
局部内部类 直接访问
如果名称相同,使用 外部类.this.成员 访问外部类的成员
创建对象,再访问(注意:必须在作用域内) 不能访问 ---> 局部内部类(因为 局部内部类地位是一个局部变量)
匿名内部类【没有名字】 创建对象,再访问(注意:必须在作用域内)
成员内部类(没有 staic 修饰) 第一种方式
// outer001.new Inner001();相当于把 new Inner001()当作是 Outer001 成员
// 第二种方式 在外部类中,编写一个方法,可以返回一个Inner001对象
Outer001.Inner001 inner001Instance = outer001.getInner001Instance();
静态内部类(使用 static 修饰) 重名的话:
使用 外部类名.成员【本质;静态内部类只能访问外部类的静态成员】
// 方式1
// 因为静态内部类,是可以通过类名直接访问(前提是满足访问权限(非private))
Outer002.Inner002 inner002 = new Outer002.Inner002().;
inner002.say();

// 方式2
// 编写一个方法,可以返回静态内部类的对象实例
new Outer002().getInner002Instance().say();

类的五大成员:

  • 属性
  • 方法
  • 构造器
  • 代码块
  • 内部类
位置 作用范围 默认值 所在内存位置 生命周期不同
成员变量 属于每个类,描述类的属性 作用于每个类 系统会根据该变量的类型赋予其相应的 默认值,不手动赋值,也不会报错 在对象创建以后存在于堆中,对象回收时,成员变量消失 随对象的创建而创建,对象回收时,成员变量消失
局部变量 在每个方法内部使用,出了方法不可用 作用在方法内部 声明之后系统不会赋予默认值,必须自己手动赋值,否则会报错 在方法被调用时存在于栈中,方法调执行结束,从栈中清除 随着方法的调用被创建,方法执行结束后,从栈中清除

成员变量 VS 局部变量【局部变量在使用前必须被程序员主动初始化值】

  • 成员变量是指在一个类中定义的变量,比如:

    public class Dog {
        //	这些都是成员变量,用于描述类本身所具有的一些属性
        String breed;
        String name;
        String color;
        int age;
    }
    
  • 局部变量是指在方法或者方法代码块中定义的变量,其作用域是其所在的代码块。可分为以下三种:

    • 方法参数变量(形参):在整个方法内有效。

      //	定义了一个 testFun() 方法,该方法中包含一个 int 类型的参数变量 n,其作用域是 testFun() 方法体内。当调用方法时传递进了一个参数 3,因此其输出控制台的 n 值是 3。
      public class Test3 {
          public static void testFun(int n) {
              System.out.println("n=" + n);
          }
      
          public static void main(String[] args) {
              testFun(3);
          }
      }
      
    • 方法局部变量(方法内定义): 从定义这个变量开始到方法结束这一段时间内有效。

      // 声明两个局部变量并输出其值,其实现代码如下:
      public class Test2 {
          public static void main(String[] args) {
              int a = 7;
              if (5 > 3) {
                  int s = 3; // 声明一个 int 类型的局部变量
                  System.out.println("s=" + s);
                  System.out.println("a=" + a);
              }
              System.out.println("a=" + a);
          }
      }
      //	上述实例中定义了 a 和 s 两个局部变星,其中 int 类型的 a 的作用域是整个 main() 方法,而 int 类型的变量 s 的作用域是 if 语句的代码块内
      
    • 代码块局部变量(代码块内定义):从定义这个变量开始到代码块结束这一段时间内有效。

      // 代码块局部变量常用于 try catch 代码块中,成为异常处理参数变量。
      // 异常处理参数变量的作用域是在异常处理块中,该变量是将异常处理参数传递给异常处理块,与方法参数变量类似。
      // 声明一个异常处理语句,实现代码如下:
      public class Test4 {
          public static void test() {
              try {
                  System.out.println("Hello!Exception!");
              } catch (Exception e) { // 异常处理块,参数为 Exception 类型
                  e.printStackTrace();
              }
          }
      
          public static void main(String[] args) {
              test();
          }
      }
      //	在上述实例中定义了异常处理语句,异常处理块 catch 的参数为 Exception 类型的变量 e,作用域是整个 catch 块。
      

成员变量

修饰 访问 声明周期
全局变量 无 static 对象名.变量名 只要对象被当做引用,实例变量就存在
静态变量 有 static 类名.变量名
对象名.变量名
其生命周期取决于类的生命周期
类被GC回收时才会被销毁

1. 基本介绍

一个类的内部又完整地嵌套了另一个类结构,被嵌套的类称为内部类(in ner class)

嵌套其它类的类称为外部类(outer class)

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

2. 基本语法

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

class Other{	//	外部其它类
    
}

3. 快速入门

package com.innerclass;

public class InnerClass01 { //  外部其他类
    public static void main(String[] args) {

    }
}

class Outer{    //  外部类

    private int n1 = 100;   //  属性

    public Outer(int n1) {  //  构造器
        this.n1 = n1;
    }

    public void m1(){   //  方法
        System.out.println("m1()");
    }

    //  代码块
    {
        System.out.println("代码块....");
    }

    class Inner{    //  内部类,在Outer类的内部

    }
}

4. 内部类的分类

1. 定义在外部类局部位置上(比如方法内)

局部内部类(有类名)---> 访问外部【外部类名.this.成员】

局部内部类的使用

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

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

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

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

  5. 外部类 --- 访问 ---> 局部内部类的成员

    访问方式:创建对象,再访问(注意:必须在作用域内)

  6. 记住:

    • 局部内部类定义在方法中 / 代码块
    • 作用域在方法体或者代码块中
    • 本质仍然是一个类
  7. 外部其它类 --- 不能访问 ---> 局部内部类(因为 局部内部类地位是一个局部变量)

  8. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员变量,则可以使用

    # 解读
    外部类名.this.成员	
    
    //	解读 Outer01.this 
    //	本质就是外部类的对象
    System.out.println("n1 = " + n1 + "外部类的n1=" + Outer01.this.n1);
    
package com.innerclass;

/**
 * 演示局部内部类的使用
 */
public class LocalInnerClass {  //  外部其它类
    public static void main(String[] args) {

        //  演示一遍
        Outer01 outer01 = new Outer01();
        outer01.m1();

        System.out.println("outer01的hashcode=" + outer01);
    }
}

class Outer01 { //  外部类
    private int n1 = 100;

    private void m2() {	//  私有方法
        System.out.println("Outer01 m2()");
    }    

    public void m1() {  //  方法
        //  1)局部内部类是定义在外部类的局部位置,通常在方法

        //  3)不能添加访问修饰符,但是可以使用final 修饰【被final修饰后,该内部类就不能被其它类继承了】
        //  4)作用域:仅仅在定义它的方法(本例中即:在m1()方法内有效)或代码块中
        class Inner01 {  //  局部内部类(本质仍然是一个类)
            //  2)可以直接访问外部类的所有成员(属性,方法),包含私有的
            private int n1 = 800;
            private void m2(){
                System.out.println("Inner01 m2");
            }

            public void f1() {
                //  5)  局部内部类可以直接访问外部类的成员,比如下面 外部类 n1 和 m2()
                //  7)  如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用外部类名.this.成员
                System.out.println("n1 = " + n1 + "外部类的n1=" + Outer01.this.n1);

                System.out.println("Outer01.this hashcode=" + Outer01.this);

                Outer01.this.m2();
            }
        }
        //  6)外部类在方法m1()中,可以创建Inner01的对象,然后调用方法即可
        Inner01 inner01 = new Inner01();
        inner01.f1();
    }
}

其实在 main方法中,还是要先创建外部类,然后才能访问到内部类的

经过 debug得出:Outer01.this 就是 外部类对象

image-20230328204006514

匿名内部类(没有类名)⭐===> 是一个对象【简化开发】。用完一次就销毁,运行类型:Outer04$1 依次递增

  • 本质是类
  • 内部类
  • 该类没有名字
  • 同时还是一个对象

需求引入:想使用 IA 接口,并创建对象

interface IA{	//	接口
    publc void cry();
}

//	传统方式实现
class Tiger implements IA{
    
    @Override
    public void cry(){
        System.our.println("老虎叫唤!!!");
	}
}

//	调用
IA tiger = new Tiger();
tiger.cry();

这个 tiger 对象,我可能只会用到一次,就不用了 ===> 这样我在专门去写一个 Tiger 类就很不划算!!!

基本语法:

new 类或接口(参数列表){
    类体
}
//	使用匿名内部类来简化开发 
//	1. tiger 的编译类型:IA
//	2. tiger 的运行类型:就是匿名内部类。class com.innerclass.Outer04$1
IA tiger = new IA() {
    @Override
    public void cry() {
        System.out.println("老虎叫唤...");
    }
};
System.out.println("tiger的运行类型=" + tiger.getClass());
tiger.cry();
//	在底层可以理解为这样:
 /*
 我们看底层 会分配 类名 Outer04$1
 class ×××× implements IA{
	 @Override
	 public void cry() {
	 System.out.println("老虎叫唤...")
 }
 */
        //  jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并且把地址返回给tiger
        //  匿名内部类只能使用一次,就不能再使用了 【说的是匿名内部类只能使用一次,在底层生成完了给你返回一个实例,这个类就没有了】
//  但是这个对象(tiger)你是可以反复使用的

演示匿名内部类的使用

package com.innerclass;

public class AnonymousInnerClass {
    public static void main(String[] args) {
        Outer04 outer04 = new Outer04();
        outer04.method();

    }
}

class Outer04 { //  外部类
    private int n1 = 10;    //  属性

    public void method(){   //  方法
        //  基于接口的匿名内部类
        //  需求:想使用接口IA,并创建对象
        //  传统方式:写一个类,实现该接口,并创建对象

        //  可以使用匿名内部类来简化开发
        //  tiger的编译类型是:IA,
        //  tiger的运行类型是:就是匿名内部类 ===> Outer04$1

        /*
            我们看底层 会分配 类名 Outer04$1
            class ×××× implements IA{
                @Override
                public void cry() {
                    System.out.println("老虎叫唤...")
            }
         */
        //  jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并且把地址返回给tiger
        //  匿名内部类只能使用一次,就不能再使用了 【说的是匿名内部类只能使用一次,在底层生成完了给你返回一个实例,这个类就没有了】
        //  但是这个对象(tiger)你是可以反复使用的

        IA tiger = new IA() {
            @Override
            public void cry() {
                System.out.println("老虎叫唤...");
            }
        };
        System.out.println("tiger的运行类型=" + tiger.getClass());
        tiger.cry();

        //  演示基于类的匿名内部类
        //  分析
        //  1)father 编译类型 Father
        //  2)father 运行类型 Outer04$2
        //  3)底层会创建匿名内部类
        /*
            class Outer04$2 extends Father{
            }

         */
        //  4)同时也直接返回了 匿名内部类 Outer04$2的对象
        //  5)注意("jack")参数列表会传递给构造器
        Father father = new Father("jack"){
            @Override
            public void test() {
                System.out.println("匿名内部类重写了test方法");
            }
        };
        System.out.println("father对象的运行类型=" + father.getClass());   // Outer04$2【按照匿名内部类顺序编号从1开始】
        father.test();

        //  基于抽象类的匿名内部类 (由于是抽象类,那么这个方法 eat() 就要求你必须重写)
        Animal animal = new Animal() {
            @Override
            void eat() {
                System.out.println("小狗吃骨头...");
            }
        };

        System.out.println("animal对象的运行类型" +  animal.getClass());	// Outer04$3
        animal.eat();

    }
}


interface IA {   //  接口
    public void cry();
}

class Father {
    public Father(String name) {   //  构造器
        System.out.println("接收到name = " + name);
    }

    public void test() {    //  方法
    }
}

abstract class Animal { //  抽象类

    abstract void eat();

}

匿名内部类使用细节:

因为匿名内部类既是一个类的定义,同时本身也是一个对象,因此从语法上看:

  • 它既有类的特征
  • 也有创建对象的特征

因此,有如下2种方式 调用匿名内部类方法

package com.innerclass;

public class AnonymousInnerClassDetail {
    public static void main(String[] args) {

        Outer05 outer05 = new Outer05();
        outer05.f1();

    }
}

class Outer05 {
    private int n1 = 99;
    public void f1() {
        //  创建一个基于类的匿名内部类
        //	不能添加访问修饰符,因为它的地位就是一个局部变量
        Person person =  new Person() {
            private int n1 = 1;
            @Override
            public void hi() {
                System.out.println("匿名内部类重写了 hi()方法");
                System.out.println("n1 = " + Outer05.this.n1);
            }
        };

        person.hi();	//	动态绑定,运行类型是 Outer05$1

        //  也可以直接调用,匿名内部类本身也是返回对象
        //  class 匿名内部类 extends Person {}
        new Person(){
            @Override
            public void hi() {
                System.out.println("匿名内部类重写了 hi()方法,哈哈...");
                System.out.println("n1 = " +  n1);
            }
        }.ok("白");
    }
}

class Person {  //  类
    public void hi() {
        System.out.println("Person hi()");
    }

    public void ok(String str) {
        System.out.println("Person ok() " + str);
    }
}
匿名内部类最佳实践
  1. 将匿名内部类 当做实参 传递

    package com.innerclass;
    
    public class InnerClassExercise01 {
        public static void main(String[] args) {
    		//	当做实参直接传递,简洁高效!!!
            f1(new IQ() {
                @Override
                public void show() {
                    System.out.println("这是一副名画...");
                }
            });
        }
        //  静态方法,形参是接口类型
        public static void f1(IQ iq){
            iq.show();
        }
    }
    //  接口
    interface IQ{
        void show();
    }
    
  2. 习题

    image-20230328195047738

    package com.jjq;
    
    public class homework {
        public static void main(String[] args) {
            //	传入匿名内部类,而这个匿名内部类可以根据实际情况,来打印不同的信息
           CellPhone cellPhone = new CellPhone();
            cellPhone.alarmClock(new Bell() {
                @Override
                public void ring() {
                    System.out.println("懒猪起床了");
                }
            });
    
            cellPhone.alarmClock(new Bell() {
                @Override
                public void ring() {
                    System.out.println("小伙伴上课了");
                }
            });
        }
    }
    
    class CellPhone{
        public void alarmClock(Bell bell){	//	形参是接口类型
            bell.ring();
        }
    }
    
    
    interface Bell{
        void ring();
    }
    

2. 定义在外部类的成员位置上

成员内部类(没有 static 修饰)

可以添加访问修饰符

  • public
  • porotected
  • 默认
  • private
package com.innerclass;

public class MemberInnerClass01 {
    public static void main(String[] args) {
        Outer001 outer001 = new Outer001();	//	外部类使用成员内部类
        outer001.t1();
        
        //  外部其它类,使用成员内部类的三种方式
        //  解读
        //  第一种方式
        //  outer001.new Inner001();相当于把 new Inner001()当作是 Outer001 成员
        //  这就是一个语法,不用特别纠结
        Outer001.Inner001 inner001 = outer001.new Inner001();
        inner001.say();


        //  第二种方式 在外部类中,编写一个方法,可以返回一个Inner001对象
        Outer001.Inner001 inner001Instance = outer001.getInner001Instance();
        inner001Instance.say();
        
        //	第三种【是对第二种的简写】
        new Outer001().getInner001Instance().say();
    }
}

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

    //  注意:成员内部类,是定义在外部类的成员位置上
    class Inner001 {    //  成员内部类
        public void say(){
            //  可以直接访问外部类的所有成员,包含私有的
            System.out.println("Outer001 的 n1 = " + n1 + "  Outer001 的 name " + name);
        }
    }

    //  方法,返回一个Inner001的实例
    public Inner001 getInner001Instance() {
        return new Inner001();
    }

    //  写方法
    public void t1() {
        //  使用成员内部类
        Inner001 inner001 = new Inner001();
        inner001.say();
    }
}

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

  1. 可以访问外部类的所有静态成员,包含私有的
  2. 可以添加任意访问修饰符(public, protected, 默认, private),因为它的地位就是一个成员
  3. 作用域:同其它成员,为整个整体
package com.innerclass;

public class StaticInnerClass01 {
    public static void main(String[] args) {

        //  外部其它类 使用静态内部类

        //  方式1
        //  因为静态内部类,是可以通过类名直接访问(前提是满足访问权限(非private))
        Outer002.Inner002 inner002 = new Outer002.Inner002();
        inner002.say();

        //  方式2
        //  编写一个方法,可以返回静态内部类的对象实例
        new Outer002().getInner002Instance().say();

        Outer002.getInner002Instance2().say();

    }
}

class Outer002{ //  外部类
    private int n1 = 10;
    private static String name = "白白";

    //  Inner002 就是静态内部类
    //  1)放在外部类的成员位置
    //  2)使用static 修饰
    //  3)可以直接访问外部类的所有静态成员,包含私有的,但是不能访问非静态成员
    //  4)可以添加任意访问修饰符(public,protected,默认,private),因为它的地位就是一个成员
    //  5)作用域:同其它的成员,为整个整体
    static class Inner002{  //  内部类
        public void say(){
            System.out.println(name);

        //  不能直接访问外部类的非静态成员
	    //  System.out.println(n1);
        }
    }

    public void show() {
        //  外部类使用内部类
        new Inner002().say();
    }

    //  写一个方法返回静态内部类的实例
    public Inner002 getInner002Instance() {
        return new Inner002();
    }

    //  编写一个静态方法返回 (静态内部类的实例)
    public static Inner002 getInner002Instance2(){
        return new Inner002();
    }
}

标签:内部,void,System,public,println,class
From: https://www.cnblogs.com/aclq/p/17268503.html

相关文章