首页 > 技术文章 > 面向对象(五)

wyzstudy 2021-10-02 19:21 原文

内部类

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

class Outer{//外部类
    private int n1 = 100;//成员属性
    public void m1(){//成员方法
        System.out.println("m1方法");
    }
    {//代码块
        System.out.println("代码块");
    }
    public Outer(int n1) {//构造器
        this.n1 = n1;
    }
    class Inner{//内部类
        
    }
}

内部类的分类:
定义在外部类局部位置上(比如方法体内):

  • 局部内部类
  • 匿名内部类(重点!!)

定义在成员属性的位置上:

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

局部内部类

①定义的位置在局部变量的位置上,一般定义在方法体内部,也可以定义在代码块中,本质仍然是一个类

②可以直接访问外部类的所有属性,包括私有属性

③不能添加访问修饰符,因为局部类部类跟局部变量一样,但是可以使用final修饰

④作用域在定义它的方法体内部

⑤内部类访问外部类的属性直接访问即可

⑥外部类访问内部类的属性,先创建对象,再访问

⑦外部其它类不能访问局部内部类,局部内部类就是一个局部变量

⑧当如果局部内部类和外部类的属性重名,访问的时候遵守就近原则,如果想要访问外部类的属性可以使用外部类名.this.属性
外部类名.this指代的就是正在调用当前内部类包含方法的对象

class Outer2{
    private int n1 = 100;
    private void m2(){
        System.out.println("Outer2 m2()");
    }
    public void m1(){
        class Inner{//局部内部类,可以直接访问外部类的所有属性,包括私有属性
            private int n1 = 800;
            public void f1(){
                System.out.println("n1 = " + n1);
                //访问同名的外部类属性
                System.out.println("Outer2 n1 = " + Outer2.this.n1);
                m2();
            }
        }
        //外部类访问局部内部类,直接在方法体内创建对象访问
        Inner inner = new Inner();
        inner.f1();
    }
}

匿名内部类(!!!!!)

匿名内部类的初步解读:没有名字;是一个内部类;本质还是类;同时还是一个对象

①匿名内部类使用一次就不能使用

②在jdk底层创建内部类的时候,马上就创建了一个实例

匿名内部类可以简化开发

//基于接口的匿名内部类
interface IA{
    public void cay();
}
class Outer3{
    //外部类
    private int n1 = 100;
    public void m1(){
        //tiger的编译类型是tiger
        //运行时类型是匿名内部类
        IA tiger = new IA(){

            @Override
            public void cay() {
                System.out.println("老虎在叫喊");
            }
        };
        tiger.cay();
        //tiger的运行时类型:class innerclass.Outer3$1
        System.out.println("tiger的运行时类型:" + tiger.getClass());
    }
}
//基于类的匿名内部类
class Father{
    private String name;
    public Father(String name){
        this.name = name;
    }
    public void eat(){

    }
}
class Outer3{
    //外部类
    private int n1 = 100;
    public void m1(){
        //father的编译类型是Father,运行时类型是匿名内部类
        //这里的形参列表是传递给Father的构造器
        //这里是匿名内部类,不是Father类型
       Father father = new Father("jack"){
           @Override
           public void eat() {
               System.out.println("匿名内部类重写eat方法");
           }
       };
       father.eat();
       System.out.println("Father的运行时类型:" + father.getClass());
    }
}
//基于抽象类的匿名内部类
class Outer3{
    //外部类
    private int n1 = 100;
    public void m1(){
       //基于抽象类的匿名内部类,编译类型是Animal,运行时类型是匿名内部类
        Animal animal = new Animal() {
            @Override
            void eat() {
                System.out.println("小狗吃骨头");
            }
        };
        animal.eat();
    }
}
abstract class Animal{
    abstract void eat();
}

④匿名内部类的两种访问方式:

//第一种
Animal animal = new Animal() {
    @Override
    void eat() {
        System.out.println("小狗吃骨头");
    }
};
animal.eat();

//第二种
new Animal(){
   @Override
    void eat() {
        System.out.println("小狗吃骨头");
    } 
}.eat();

⑤匿名内部类可以直接访问外部类的所有属性

⑥外部其它类是不能够创建匿名内部类的

⑦匿名内部类的作用域仅仅在它所在的代码块或者方法体中

⑧如果匿名内部类的属性和外部类属性同名,遵守就近原则,如果想要访问外部类的属性可以使用外部类名.this.属性访问

匿名内部类的实践:
1.当做实参传递

public class Demo1 {
    public static void main(String[] args) {
        //传入一个匿名内部类对象
        f1(new IA(){

            @Override
            public void show() {
                System.out.println("使用匿名内部类实现传入参数");
            }
        });
    }
    public static void f1(IA ia){
        ia.show();
    }
}
interface IA{
    public void show();
}

成员内部类

成员内部类定义在外部类的成员属性的位置上,没有static修饰

class Outer4{
    private int n1 = 200;
    private String name = "张三";

    public class Inner{
        public void m1(){
            //访问外部类的私有属性
            System.out.println("n1 = " + n1 + " name = " + name);
        }
    }

    public void f1(){
        //在外部类方法体内部使用成员内部类
        Inner inner = new Inner();
        inner.m1();
    }
}

成员内部类可以访问外部类的所有属性,包括私有属性
成员内部类因为在类成员的位置上面,所有可以使用访问修饰符:public、protected、默认、private修饰
成员内部类的作用域在整个类中,跟其它成员一样
成员内部类访问外部类的属性,直接访问,如果成员内部类和外部类的属性出现同名,遵守就近原则,如果想要访问外部类的属性,可以使用外部类.this.属性
外部类想要访问内部类的属性,先创建实例,再访问,可以访问成员内部类的私有属性,在同一个类中

class Outer4{
    private int n1 = 200;
    private String name = "张三";

    public class Inner{
        private int n1 = 100;
        public void m1(){
            //访问外部类的私有属性
            System.out.println("n1 = " + n1 + " name = " + name);
            System.out.println("访问外部类属性 n1= " + Outer4.this.n1);
        }
    }

    public void f1(){
        //在外部类方法体内部使用成员内部类
        Inner inner = new Inner();
        inner.m1();
        //访问成员内部类的私有属性
        System.out.println("成员内部类的 n1 = " + inner.n1);
    }
}

外部其它类想要访问成员内部类有两种方式:

 //第一种方式,当成一个成员属性来创建,通过一个外部类的实例
Outer4 outer4 = new Outer4();
Outer4.Inner inner = outer4.new Inner();
inner.m1();
System.out.println("===========");
//第二种方式,在外部类提供一个返回内部类实例的方法
Outer4.Inner inner1 = outer4.getInstanceInner();
inner1.m1();

静态内部类

成员内部类定义在外部类的成员属性的位置上,有static修饰

静态内部类可以访问外部类所有的静态成员,包括私有的
静态内部类可以使用访问修饰符,public,默认,protected,private
静态内部类的作用域在整个类中

class Outer5{
    private int n1 = 100;
    private static String name = "张三";
    //静态内部类
    static class Inner{
        public void m1(){
            //静态内部类访问外部类的静态属性
            System.out.println("name = " + name);
        }
    }
    public void f1(){
        Inner inner = new Inner();
        inner.m1();
    }
}

外部类访问静态内部类,先创建实例,再访问
静态内部类访问外部类的属性,直接访问,如果存在同名属性,遵守就近原则,如果想要访问外部类的静态属性,可以使用外部类.属性即可
外部其它类访问静态内部类的方式:

 //第一种方式
Outer5.Inner inner = new Outer5.Inner();
inner.m1();
System.out.println("=======");
//第二种方式,提供一个方法返回实例
Outer5.Inner inner1 = Outer5.getInstanceInner();
inner1.m1();

枚举

自定义枚举类型:

//自定义枚举类型
class Session{
    private String name;
    private String desc;
    //内部创建所有实例
    public static final Session SPRING = new Session("春天","春天的天");
    public static final Session SUMMER = new Session("夏天","夏天的天");
    public static final Session AUTUMN = new Session("秋天","秋天的天");
    public static final Session WINTER = new Session("冬天","冬天的天");

    //构造器私有化,防止外部再创建实例
    private Session(String name,String desc){
        this.name = name;
        this.desc = desc;
    }
    //属性为只读,不能修改,所有只提供属性的get方法

    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    @Override
    public String toString() {
        return "Session{" +
                "name='" + name + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }
}

1.构造器私有化,不能让外部创建实例

2.属性只提供get方法,不能修改属性

3.在内部创建好实例,使用static final修饰

使用enum定义枚举类型

enum Session1{

    //定义使用到的常量,使用,分割
    SPRING("春天","春天的天"),
    SUMMER("夏天","夏天的天"),
    AUTUMN("秋天","秋天的天"),
    WINTER("冬天","冬天的天");

    private String name;
    private String desc;

    //构造私有化
    private Session1(String name,String desc){
        this.name = name;
        this.desc = desc;
    }

    //提供get方法,访问呢属性,不能提供set修改属性

    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    @Override
    public String toString() {
        return "Session1{" +
                "name='" + name + '\'' +
                ", desc='" + desc + '\'' +
                '}';
    }
}

1.自定义枚举类型,底层使用final修饰,继承自Enum类
2.自定义枚举对象必须放在枚举类的行首
3.当使用无参函数构造枚举对象的时候,可以省略参数列表和括号
4.使用enum定义的枚举类不能再继承其它类,因为隐式继承了Enum类
5.可以去实现其它接口
使用反汇编执行javap查看源码:

Enum类常用方法

1.name() 返回枚举对象的名字
2.ordinal()返回枚举对象的编号,在定义的时候就确定了,从0开始
3.values()返回所有的枚举对象的数组,可以通过反汇编指令查看源代码发现
4.valueOf(String)将一个字符串转换成枚举对象,这个字符串必须是枚举类定义过的枚举对象
5.compareTo()比较的是两个枚举类型的编号,用当前编号减参数的编号

System.out.println(Session1.SPRING.name());
System.out.println(Session1.SPRING.ordinal());
for(Session1 session1: Session1.values()){
    System.out.println(session1);
}
Session1 Spring = Session1.valueOf("SPRING");
System.out.println(Spring);

System.out.println(Session1.SPRING.compareTo(Session1.SUMMER));
/*
SPRING
0
Session1{name='春天', desc='春天的天'}
Session1{name='夏天', desc='夏天的天'}
Session1{name='秋天', desc='秋天的天'}
Session1{name='冬天', desc='冬天的天'}
Session1{name='春天', desc='春天的天'}
-1
*/

注解

注解被称为元数据,用于修饰解释包、类、方法、属性、构造器、局部变量等数据信息

和注释一样,注释不影响程序逻辑,但是注解可以被编译和运行,相当于嵌入到代码的补充信息

@interface 不是接口,是注解类,在JDK1.5中加入

修饰注解的注解称为元注解

  • @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
  • @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
  • @SuppressWarnings - 指示编译器去忽略注解中声明的警告。

作用在其他注解的注解(或者说 元注解)是:

  • @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
  • @Documented - 标记这些注解是否包含在用户文档中。
  • @Target - 标记这个注解应该是哪种 Java 成员。
  • @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

推荐阅读