17-内部类的基本概念

17-内部类的基本概念,第1张

17-内部类的基本概念
内部类的基本概念
内部类的概念和分类------------------------------- 当一个类的定义出现在另外一个类的类体中时,那么这个类叫做内部类(Inner) 而这个内部类所在的类叫做外部类(Outer) 类中的内容:成员变量,成员方法,构造方法,静态成员,构造块和静态代码块,内部类 实际作用------------------------------- 当一个类存在的价值仅仅是为某一个类单独服务时,那么就可以将这个类定义为所服务类中的内部类 这样可以隐藏该类的实现细节并且可以方便的访问外部类的私有成员而不再需要提供公有的get和set方法 普通内部类:直接将一个类的定义放在另外一个类的类体中 静态内部类:使用static关键字修饰的内部类,隶属于类层级 局部内部类:直接将一个类的定义放在方法体的内部时 匿名内部类:就是指没有名字的内部类 普通(成员)内部类的格式-------------------------------
访问修饰符 class 外部类的类名{
    访问修饰符 class 内部类的类名{
        内部类的类体;
    }
}
普通内部类和普通类一样可以定义成员变量,成员方法以及构造方法等 普通内部类和普通类一样可以使用final或者abstract关键字修饰 普通内部类还可以使用private或protected关键字修饰 普通内部类需要使用外部类对象来创建对象 如果内部类访问外部类中与本类内部同名的成员变量或方法时,需要使用this关键字
package com.lagou.task10;



public class NormalOuter {

        private int cnt = 1;

        //定义普通内部类,隶属于外部类的成员,并且是对象层级
         public  class NormalInner{
            //final防止其他内部类的继承
        private int ia = 2;
        private int cnt = 3;
        public NormalInner(){
            System.out.println("普通内部类的构造方法体执行到了");
        }
        public void show(){
            System.out.println("外部类中的变量cnt的数值为:" + cnt); //1
            System.out.println("ia = " + ia); //2
        }
        public void show2(int cnt){
            System.out.println("形参变量cnt = " + cnt); //局部优先原则
            System.out.println("内部类的cnt = " + this.cnt); //3
            System.out.println("外部类的cnt = " + NormalOuter.this.cnt); //1
            //NormalOuter.this:上级引用类型的this,no.new NormalInner();,即no的类型
        }

        }
}

package com.lagou.task10;


public class NormalOuterTest {
    public static void main(String[] args) {
        //声明NormalOuter类型的引用指向该类型的对象
        NormalOuter no = new NormalOuter();
        //声明NormalOuter类中内部类的引用指向内部类的对象
        NormalOuter.NormalInner ni = no.new NormalInner();
        //NormalOuter.NormalInner ni = new NormalOuter.NormalInner();
        //报错,之所以报错,是因为内部类需要访问外部类的东西
        //若这样可以的话,那么ni就是一个单独的引用了,即只可以访问这个类的东西了
        //即访问不到外部类的东西,所以规定,必须要有外部类的引用来创建内部类的对象
        //使得该对象是外部类的东西
        //调用内部类中的show方法
        ni.show();
        System.out.println("------------------");
        ni.show2(4);
    }
}

静态内部类的格式-------------------------------
访问修饰符 class 外部类的类名{
    访问修饰符 static class 内部类的类名{
        内部类的类体;
    }
}
静态内部类不能直接访问外部类的非静态成员 静态内部类可以直接创建对象 如果静态内部类访问外部类中与本类内同名的成员变量或方法时,需要使用类名.的方式访问
package com.lagou.task10;


public class StaticOuter {
    private int cnt = 1;        // 隶属于对象层级
    private static int snt = 2; // 隶属于类层级

    public  void show() {
        System.out.println("外部类的show方法就是这里!");
    }

    
    public static class StaticInner {
        private int ia = 3;
        public static int snt = 4;

        public StaticInner() {
            System.out.println("静态内部类的构造方法哦!");
        }

        public void show() {
            System.out.println("ia = " + ia); // 3
            System.out.println("外部类中的snt = " + snt); // 2
            //System.out.println("外部类的cnt = " + cnt);
            //Error:静态上下文中不能访问非静态的成员,因此此时可能还没有创建对象
        }

        public void show2(int snt) {  // 就近原则
            System.out.println("snt = " + snt); // 5
            System.out.println("内部类中的成员snt = " + StaticInner.snt); // 4
            //静态里面可以有静态,即有两种方式访问
            //若其他类的名字也是StaticInner,那么由于就近原则,会先使用自己类的静态变量
            System.out.println("外部类中的成员snt = " + StaticOuter.snt); // 2
            //StaticOuter.show();
            new StaticOuter().show();
        }
    }
}

package com.lagou.task10;

public class StaticOuterTest {

    public static void main(String[] args) {

        // 1.声明StaticInner类型的引用指向该类型的对象
        StaticOuter.StaticInner si = new StaticOuter.StaticInner();
        //内部类写上后,他的名字就是StaticOuter.StaticInner
        //而不是StaticInner,因为需要访问时,必须穿过内部类的外部类才可
        // 2.调用show方法进行测试
        si.show();

        System.out.println("---------------------------------------------");
        si.show2(5);
    }
}

局部(方法)内部类的格式-------------------------------
访问修饰符 class 外部类的类名{
    访问修饰符 返回值类型 成员方法名(形参列表){
        class 内部类的类名{ //之所以没有修饰符,因为方法调用完就释放了
            //即只与方法有关
            内部类的类体;
        }
    }
}
局部内部类的使用方式------------------------------- 局部内部类只能在该方法的内部可以使用 局部内部类可以在方法体内部直接创建对象 局部内部类不能使用访问控制符和static关键字修饰符 局部内部类可以使用外部方法的局部变量,但是必须是final的,由局部内部类和局部变量的声明周期不同所致 局部变量不可以用private和static修饰符
package com.lagou.task10;


public class AreaOuter {
    private int cnt = 1;
    public void show(){
        //定义一个局部变量进行测试
        //从Java8开始默认理解为final关键字修饰的内容
        //虽然可以省略final关键字,但建议还是加上
       final int ic = 4;

        //定义局部内部类,只在当前方法体的内部好使
        //底层代码里,内部类会拷贝一份ic
        //即必须加final,怕我拷贝完时,你ic改变
        class AreaInner{
            private int ia = 2;
            public AreaInner(){
                System.out.println("局部内部类的构造方法");
            }
            public void test(){
                System.out.println("ia = " + ia); //2
                System.out.println("cnt = " + cnt); //1
                //ic = 5; Error
                System.out.println("ic = " + ic); //4
            }
        }
        //声明局部内部类的引用指向局部内部类的对象
        AreaInner ai = new AreaInner();
        //之所以可以这样是因为他在方法里,方法是单独的空间
        //即可以直接创建对象
        ai.test();
    }
}

package com.lagou.task10;


public class AreaOuterTest {
    public static void main(String[] args) {
        //声明外部类类型的引用指向外部类的对象
        AreaOuter ao = new AreaOuter();
        //通过show方法的调用实现局部内部类的定义和使用
        ao.show();
    }
}

回调模式的概念------------------------------- 回调模式是指:如果一个方法的参数是接口类型,则在调用该方法时,需要创建并传递一个实现此接口类型的对象 而该方式在运行时会调用到参数对象中所实现的方法(接口中定义的)
package com.lagou.task10;


public interface AnonymousInterface {
    //自定义抽象方法
    public abstract void show();


}

package com.lagou.task10;


public class AnonymousInterfaceImpl implements AnonymousInterface{

    @Override
    public void show() {
        System.out.println("这里是接口的实现类");
    }
}

package com.lagou.task10;


public class AnonymousInterfaceTest {
    //假设已有下面的方法,请问如何调用下面的方法
    //AnonymousInterface ai = new AnonymousInterfaceImpl();
    //接口类型的引用指向实现类型的对象,形成了多态
    public static void test(AnonymousInterface ai){
        //编译阶段调用父类版本,运行阶段调用实现类重写的版本
        ai.show();
    }

    public static void main(String[] args) {
        //AnonymousInterfaceTest.test(new AnonymousInterface()); //Error:接口不能实例化
        AnonymousInterfaceTest.test(new AnonymousInterfaceImpl());
        //回调:我把我对象传给你,然后你又根据我的对象回来调用我内部的方法
        System.out.println("--------------------");
        //使用匿名内部类的语法格式来得到接口类型的引用
        //格式为:接口/父类类型 引用变量名 = new 接口/父类类型(){方法的重写};
        AnonymousInterface ait = new AnonymousInterface() {
            @Override
            public void show() {
                System.out.println("匿名内部类就是这么玩的");
            }
        };
        //优势new完对象这个类的价值就不存在了,就可以销毁他的存储空间了
        //从Java8开始提出新特性Lambda表达式可以简化上述代码
        //格式为:(参数列表) ->{方法体}
        AnonymousInterface ait2 = () -> System.out.println("Lamda表达式");
        //只可用于重写一个方法的接口/父类类型
        AnonymousInterfaceTest.test(ait2);
    }
}

匿名内部类的使用------------------------------- 当接口/类类型的引用作为方法的形参时,实参的传递方式有两种 自定义类实现接口/继承类并重写方法,然后创建该类对象作为实参传递 使用上述匿名内部类的语法格式得到接口/类类型的引用即可 匿名内部类的语法格式
接口/父类类型 引用变量名 = new 接口/父类类型(){方法的重写};
枚举的基本概念------------------------------- 一年中的所有季节:春季,夏季,秋季,冬季 所有的性别:男,女 键盘上的所有方向按键:向上,向下,向左,向右 在日常生活中这些事物的取值只有明确的几个固定值,此时描述这些事物的所有值都可以一一列举出来 而这个列举出来的类型就叫做枚举类型
package com.lagou.task10;


public class Direction {
    private final String desc; //用于描述方向字符串的成员变量

    //声明本类类型的引用指向本类类型的对象
    public final static Direction UP = new Direction("向上");
    public final static Direction DOWN = new Direction("向下");
    public final static Direction LEFT = new Direction("向左");
    public final static Direction RIGHT = new Direction("向右");
    
    //这里只有四个对外开发的对象,即有限个对象
    //那么这样的类就叫做枚举
    //通过构造方法实现成员变量的初始化,更加灵活
    //私有化构造方法,此时该构造方法只能在本类的内部使用
    private Direction(String desc){
        this.desc = desc;
    }
    //通过公有的get方法可以在本类的外部访问该类成员变量的数值
    public String getDesc() {
        return desc;
    }

}

package com.lagou.task10;


public class DirectionTest {
    public static void main(String[] args) {
        
        //Direction.UP = 2; 类型不匹配
        //Direction d2 = null;
        //Direction UP = d2; Error:final关键字修饰
        Direction d1 = Direction.UP;
        System.out.println("获取到的方向是:" + d1.getDesc()); //向上
        System.out.println("-------------------------");
        //使用一下Java5开始的枚举类型
        DirectionEnum de = DirectionEnum.DOWN;
        System.out.println("获取到的方向是:" + de.getDesc());
    }
}

枚举的定义------------------------------- 使用public static final表示的常量描述较为繁琐,使用enum关键字来定义枚举类型取代常量 枚举类型是从Java5开始增加的一种引用数据类型 枚举值就是当前类的类型,也就是指向本类的对象,默认使用public static final修饰,因此采用枚举类型.的方式调用 枚举类可以自定义构造方法,但是构造方法的修饰符必须是private,默认也是私有的
package com.lagou.task10;


public enum DirectionEnum implements DirectionInterface{
    //声明本类类型的引用指向本类类型的对象
    //enum枚举类型自动初始化好了,不需要再去初始化了
    //枚举类型要求所有枚举值必须放在枚举类型的最前面
    //匿名内部类的语法格式:接口/父类类型 引用变量名 = new 接口/父类类型(){方法的重写};
    //public final static DirectionEnum DOWN = new DirectionEnum("向上");
    UP("向上"){
        @Override
        public void show() {
            System.out.println("贪吃蛇向上移动了一下");
        }
    },DOWN("向下") {
        @Override
        public void show() {
            System.out.println("贪吃蛇向下移动了一下");
        }
    },LEFT("向左") {
        @Override
        public void show() {
            System.out.println("左移了一下");
        }
    },RIGHT("向右") {
        @Override
        public void show() {
            System.out.println("右移了一下");
        }
    };
    private final String desc; //用于描述方向字符串的成员变量


   
    //这里只有四个对外开发的对象,即有限个对象
    //那么这样的类就叫做枚举
    //通过构造方法实现成员变量的初始化,更加灵活
    //私有化构造方法,此时该构造方法只能在本类的内部使用
    private DirectionEnum(String desc){
        this.desc = desc;
    }
    //通过公有的get方法可以在本类的外部访问该类成员变量的数值
    public String getDesc() {
        return desc;
    }
    //整个枚举类型只重写一次,所有对象调用同一个
    
}

自定义类和枚举类型在switch结构的使用-------------------------------
package com.lagou.task10;


public class DirectionUseTest {
    //自定义静态方法实现根据参数指定的字符串内容来打印具体的方向信息
    public static void test1(String str){
        switch(str){
            case "向上":
                System.out.println("抬头望明月"); break;
            case "向下":
                System.out.println("低头思故乡"); break;
            case "向左":
                System.out.println("左牵黄"); break;
            case "向右":
                System.out.println("右擎苍"); break;
            default:
                System.out.println("没有这样的方法哦");

        }
    }
    public static void test2(DirectionEnum de){
        //switch支持枚举类型
        //即这里的UP,DOWN,LEFT,RIGHT可以不用类.的方式访问
        switch(de){
            case UP:
                System.out.println("抬头望明月"); break;
            case DOWN:
                System.out.println("低头思故乡"); break;
            case LEFT:
                System.out.println("左牵黄"); break;
            case RIGHT:
                System.out.println("右擎苍"); break;
            default:
                System.out.println("没有这样的方法哦");

        }
    }


    public static void main(String[] args) {

        DirectionUseTest.test1(Direction.UP.getDesc());
        DirectionUseTest.test1("今天好哈");
        System.out.println("--------------------------");
        DirectionUseTest.test2(DirectionEnum.DOWN);
        //DirectionUseTest.test2("今天好哈"); Error:类型不匹配,减少了出错的可能性
        //由于DirectionEnum de = DirectionEnum.DOWN;
        //其中DOWN是DirectionEnum类型的引用
        //如public final static DirectionEnum DOWN = new DirectionEnum("向下");

    }
}

Enum类的概念和常用方法------------------------------- 所有的枚举类都继承自java.lang.Enum类,常用方法如下

package com.lagou.task10;


public class DirectionEnumTest {

    public static void main(String[] args) {
        //获取DirectionEnum类型中的所有的枚举对象
        DirectionEnum[] arr = DirectionEnum.values();
        //打印每个枚举对象在枚举类型中的名称和索引位置
        for(int i =0; i < arr.length; i++){
            System.out.println("获取到的枚举对象名称是:" + arr[i].toString());
            System.out.println("获取到的枚举对象对应的索引位置是:" + arr[i].ordinal());
        }
        System.out.println("--------------------");
        //根据参数指定的字符串得到枚举类型的对象,也就是将字符串转换为对象
        //DirectionEnum de = DirectionEnum.valueOf("向下");
        //DirectionEnum de = DirectionEnum.valueOf("UP LEFT");
        //要求字符串名称必须在枚举对象中存在
        //编译ok,运行IllegalArgumentException非法参数异常
        DirectionEnum de = DirectionEnum.valueOf("DOWN");
        //System.out.println("转换出来的枚举对象名称是:" + de.toString());
        System.out.println("转换出来的枚举对象名称是:" + de);
        //当打印引用变量时,会自动调用toString()方法
        System.out.println("--------------------");
        //使用获取到的枚举对象与枚举类中已有的对象比较先后顺序
        for(int i = 0; i< arr.length; i++){
            //当调用对象在参数对象之后时,获取到的比较结果为正数
            //当调用对象在参数对象相同位置时,获取到的比较结果为0
            //当调用对象在参数对象之前时,获取到的比较结果为负数
            //比如我的位置为1,则有参数位置为0,3,那么结果为1,-2
            System.out.println("调用对象与数组中对象比较的先后顺序结果是:" + de.compareTo(arr[i]));
        }
        System.out.println("--------------------");
        //使用数组中每个DirectionEnum对象都去调用show方法测试
        for(int i = 0; i 
枚举类实现接口的方式------------------------------- 
枚举类默认继承java.lang.Enum类,即只能实现接口 
枚举类实现接口后需要重写抽象方法,而重写方法的方式有两种:重写一个,或者每个对象都重写 
注解的基本概念------------------------------- 
注解(Annotation)又叫标注,是从Java5开始增加的一种引用数据类型 
枚举可以看成一种特殊的类,而注解可以看成一种特殊的接口 
注解本质上就是代码中的特殊标记,通过这些标记可以在编译,类加载,以及运行时执行指定的处理 
注解的语法格式------------------------------- 
访问修饰符 @interface 注解名称{
    注解成员
}
自定义注解自动继承java.lang.annotation.Annotation接口 通过@注解名称的方式可以修饰包,类,成员方法,成员变量,构造方法,参数,局部变量的声明等 注解的使用方式------------------------------- 注解体中只有成员变量没有成员方法,而注解的成员变量以"无形参的方法"形式来声明 其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型 如果注解只有一个参数成员,建议使用参数名为value 而类型只能是八种基本数据类型,String类型,Class类型(不是类类型),enum类型及Annotation类型 元注解的概念------------------------------- 元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其他的注解上面 只服务于注解 注解在服务与类时,不会影响到其他注解,因为他只是用来标记/标识 元注解主要有:@Retention,@documented,@Target,@Inherited,@Repeatable 元注解@Retention------------------------------- @Retention应用到一个注解上用于说明该注解的生命周期,取值如下: RetentionPolicy.SOURCE注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视(java)=>(class)x RetentionPolicy.CLASS注解只被保留到编译进行的时候,它并不会被加载到JVM中,默认方式(class)=>(JVM)x RetentionPolicy.RUNTIME注解可以保留到程序运行的时候,它会被加载进入到JVM中 所以在程序运行时可以获取到它们(JVM) 元注解@documented------------------------------- 使用javadoc工具可以从程序源代码中抽取类,方法,成员等注释形成一个和源代码配套的API帮助文档 而该工具抽取时默认不包括注解内容 在IDEA里面点击Tools工具里的Generate JavaDoc之后,选择要生成文档的类,还可以选择存放的目录 也可以在其他命令行参数(最下面的三个格子的第二个)写上-encoding utf-8设置编码 在使用的时候本代码的存放位置不可以有中文路径,存放的目录可以有中文路径 @documented用于指定注解将被javadoc工具提取成文档 定义为@documented的注解必须设置Retention值为RUNTIME 元注解@Target------------------------------- @Target用于指定被修饰的注解能用于哪些元素的修饰,取值如下:

元注解@Inherited------------------------------- @Inherited并不是说注解本身可以继承,而是说如果一个超类被该注解标记过的注解进行注解时 如果子类没有任何注解应用时,则子类就继承超类的注解 元注解@Repeatable------------------------------- @Repeatable表示自然可重复的含义,从Java8开始增加的新特性 从Java8开始对元注解@Target的参数类型ElementType枚举值增加了两个: 其中ElementType.TYPE_PARAMETER表示该注解能写在类型变量(如泛型中的T,E等)的声明语句中,如:泛型 其中ElementType.TYPE_USE表示该注解能写在使用类型的任何语句
package com.lagou.task10;

import java.lang.annotation.*;


//@Retention(RetentionPolicy.SOURCE) //表示下面的注解在源代码中有效
//@Retention(RetentionPolicy.CLASS) //表示下面的注解在字节码文件中有效,默认方式
@Retention(RetentionPolicy.RUNTIME) //表示下面的注解在运行时有效
//若一个注解中没有任何的成员,则这样的注解叫做标记注解/标识注解
@documented //表示下面的注解信息可以被javadoc工具提取到
@Target({ElementType.TYPE,ElementType.CONSTRUCTOR,
        ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER})
//表示下面的注解可以用于类型,构造方法,成员变量,成员方法的修饰
//当@Target有参数时,则默认的这些修饰会不默认,若这时删掉某个修饰,则类的那边会报关于这个修饰的错
//如这里注释掉可以修饰类的ElementType.TYPE,那么就不可以修饰类了,若没有@Target注解
//那么@MyAnnotation注解就有默认的修饰,基本上@Target的修饰都有
//即@Target可以限制注解的修饰
@Inherited //表示下面的注解修饰类时,该注解可以被子类继承
public @interface MyAnnotation {
    //shift+tab往前面,tab往后面
    //ctrl+shift+enter可以到下一行,而不用用鼠标点最后了
    //public Direction value(); //声明一个String类型的成员变量,名字为value
    //类型有要求
    //声明一个String类型的成员变量,名字为value
    public String value() default "123";
    //给这个注解写上默认值123,那么在使用注解时,可以不写该参数名
    public String value2();
}

package com.lagou.task10;



//表示将标签MyAnnotation贴在Person类的代码中
//使用注解时采用 成员参数名 = 成员参数值, ...
//@MyAnnotation 注解里没有任何成员可以这样,有成员就要有参数了
//但也可以有成员而不写参数,如给该参数写上默认值default "默认值",也可以写
//@MyAnnotation(value2 = "world")
@MyAnnotation(value = "hello",value2 = "world")
public class Person {
    
    @MyAnnotation(value2 = "1")
    private String name;
    

    private int age;

    
    @MyAnnotation(value2 = "2")
    public Person() {
    }

    
    public Person(@MyAnnotation(value2 = "3")String name, int age) {
        this.name = name;
        this.age = age;
    }

    
    @MyAnnotation(value2 = "3")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

package com.lagou.task10;


//也就是可以继承Person类的注解
//前提是该Person的注解被元注解@Inherited注解了(标识/标记)
public class Student extends Person {
}

package com.lagou.task10;

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Target;


@Repeatable(value = ManTypes.class)
//ManType本身不能多次
//即需要依赖于有ManType数组的ManTypes注解
//@Repeatable注解的参数值为class的
//即需要ManTypes来.class
@Target(ElementType.TYPE_USE)
public @interface ManType {
    String value() default "";

}

package com.lagou.task10;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;


@Target(ElementType.TYPE_USE)
public @interface ManTypes {
    ManType[] value();
}

package com.lagou.task10;


@ManType(value = "职工")
@ManType(value = "超人")
//@ManTypes({@ManType(value = "职工"),@ManType(value = "超人")})
//在Java8以前处理多个注解的方式
public class Man {
    public static void main(String[] args) {
        int ia = 97;
        char c1 = (@ManType char)ia;
    }
}

常见的预制注解-------------------------------

@Override:限定重写父类方法,该注解只能用于方法 @Deprecated:用于表示所修饰的元素(类,方法等)已过时 @SuppressWarnings:抑制编译器警告

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5691906.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存