day21【反射、注解】

day21【反射、注解】

反馈和复习
1.这两天学的不好,没有信心了
2.写代码出现一些未知异常,怎么办??? 去百度 去google 请教别人
BIO: 同步阻塞的IO, 调用某个方法时,该方法没有完成程序不能继续向下执行
NIO: 同步非阻塞的IO,调用某个方法时,无论该方法是否完成,程序可以继续向下执行,后期需要自己写代码判断
AIO: 异步非阻塞的IO,调用某个方法时,无论该方法是否完成,程序可以继续向下执行,后期会通过方法回调机制通知   
今日内容
今天的内容是重点!!!
1.反射【反射是以后基本上所有框架的底层,这是重点】
2.注解【也是给我们以后的框架使用,重点,今天我们只介绍注解的语法】    

第一章 反射

1.类加载器【了解】
  • 类的加载【重点】
    比如,现在我们定义了一个类,Student,我们使用它时候才会加载到内存
    JVM会为这个Student.class字节码文件创建一个对象,该对象我们称为Class对象(字节码文件对象)        
    

在这里插入图片描述

  • 类的加载时机

    1. 创建类的实例。
    2. 调用类的静态变量,或者为静态变量赋值。
    3. 调用类的静态方法。  
    4. 使用某个类的子类.
    5. 直接使用java.exe命令来运行某个主类 
    6. 使用反射强制加载某个类,并生成Class对象    
    
  • 类加载器的作用和分类

    我们用到类都是由类加载器负责加载到内存的!!!
    类加载器有三种:
    	启动类加载器(引导类加载器,Bootstrap ClassLoader),加载%JAVA_HOME%\bin目录下的所有类
        扩展类加载器(Extension ClassLoader),加载一些%JAVA_HOME%\jre\lib\ext扩展类
        应用程序类加载器(Application ClassLoader):用于加载我们自定义类的类加载器    
    
  • 双亲委派机制

    当某个类加载器需要加载一个类时,它并不是直接去加载,而把加载的需要交给父级类加载器,最终请求会达到到达启动类加载器,启动类加载判断是否可以加载该类,如果加载不了再交给扩展类加载器,扩展类加载判断是否可以加载,如果加载不了,再交给应用程序类加载器.
        
    双亲委派机制主要作用就是让一个类只会被加载一次!!!     
    
2.什么是反射
反射就是在程序运行时获取一个类的Class对象(字节码文件对象),从解剖它,取出其中的各种成员,使用这些成员
3.反射在实际开发中的应用
a.开发"IDEA"等之类的IDE
b.学习各种框架的设计和底层原理: Spring,SpringMVC,Mybatis,...    
4.反射中万物皆对象的概念
Class对象 --> 某个字节码文件 
Field对象 --> 某个成员变量
Method对象 --> 某个成员方法
Constructor对象 --> 某个构造方法
newInstance --> 创建对象
invoke --> 调用/执行  
    
书籍:<<深入理解JVM虚拟机>> 很有意思
    
体验一下反射的语法(和正常语法是方过来写的)
    创建对象:
		正常语法: new 构造方法(参数);
		反射语法: 构造方法对象.newInstance(参数); 

	调用方法:
		正常语法: 对象名.成员方法名(参数);
		反射语法: 成员方法对象.invoke(对象名,参数);    

在这里插入图片描述

5.反射的第一步获取字节码文件对象(Class对象)【重点】
Java定义了三种获取Class对象的方式
public class GetClassDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取一个类的Class对象的三种方式
        //1.通过类的一个静态成员 class
        Class clazz1 = Dog.class;
        System.out.println(clazz1);
        //2.通过该类的一个对象,获取该类的Class对象
        Dog dd = new Dog();
        Class clazz2 = dd.getClass();
        System.out.println(clazz2);
        //3.通过反射强制加载该类,并获取该类的Class对象
        Class clazz3 = Class.forName("com.itheima.demo02_GetClass.Dog");
        System.out.println(clazz3);
        //注意:以上是三种获取Dog类Class对象的方式,并不是获取三个Class对象,实际上他们获取的是同一个Class对象
        System.out.println(clazz1 == clazz2);
        System.out.println(clazz1 == clazz3);
        System.out.println(clazz2 == clazz3);
    }
}
输出结果:
class com.itheima.demo02_GetClass.Dog
class com.itheima.demo02_GetClass.Dog
class com.itheima.demo02_GetClass.Dog
true
true
true

6.Class对象中的三个方法
public String getName();
public String getSimple();
public Object newInstance();

public class ClassMethodDemo {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException {
        Class clazz = Dog.class;
        //Class对象中三个方法
        //1.获取全限定类名
        System.out.println(clazz.getName());
        //2.获取类名
        System.out.println(clazz.getSimpleName());
        //3.创建Class对象所代表的那个类的对象,底层实际上使用Dog的无参构造
        Dog dog = (Dog) clazz.newInstance();
        System.out.println(dog);
    }
}
输出结果:
com.itheima.demo03_ClassMathod.Dog
Dog
Dog{age=0, name='null'}

7.通过反射获取构造方法&&使用构造方法创建对象【重点】
  • 反射获取构造方法

    public Constructor getConstructor(Class... parameterTypes); 获取单个"public"构造
    public Constructor getDeclaredConstructor(Class... parameterTypes);获取单个"任意修饰"构造
        
        
    public Constructor[] getConstructors(); 获取所有"public"构造
    public Constructor[] getDeclaredConstructors();获取所有"任意修饰"构造    
        
    public class GetConstructorDemo {
        public static void main(String[] args) throws NoSuchMethodException {
            //1.反射第一步,先获取Class对象
            Dog dd = new Dog();
            Class clazz = dd.getClass();
            //2.获取单个"public"的构造方法
            Constructor con1 = clazz.getConstructor();
            System.out.println(con1);
    
    //        Constructor con2 = clazz.getConstructor(int.class, String.class);
    //        System.out.println(con2);
    
            //3.获取单个"任意修饰符"的构造方法
            Constructor con3 = clazz.getDeclaredConstructor(int.class, String.class);
            System.out.println(con3);
            System.out.println("========================");
            //4.获取所有"public"构造
            Constructor[] cons = clazz.getConstructors();
            System.out.println(cons.length);
            for (Constructor con : cons) {
                System.out.println(con);
            }
            //5.获取所有"任意修饰符"构造
            System.out.println("========================");
    
            Constructor[] conss = clazz.getDeclaredConstructors();
            System.out.println(conss.length);
            for (Constructor con : conss) {
                System.out.println(con);
            }
        }
    }    
    
    
  • 使用构造方法创建对象

    语法:
    	构造方法对象.newInstance(参数);
        @Test
        public void test02() throws Exception {
            //使用构造
            //1.反射第一步,先获取Class对象
            Dog dd = new Dog();
            Class clazz = dd.getClass();
            //2.获取单个"public"的构造方法
            Constructor con1 = clazz.getConstructor();
            System.out.println(con1);
    
            Constructor con2 = clazz.getConstructor(int.class,String.class);
            System.out.println(con2);
            //3.使用构造创建对象
            Dog dog1 = (Dog)con1.newInstance();
            System.out.println(dog1);
    
            Dog dog2 = (Dog)con2.newInstance(10,"旺财");
            System.out.println(dog2);
        }
    输出结果:
    public com.itheima.demo04_GetConstructor.Dog()
    public com.itheima.demo04_GetConstructor.Dog(int,java.lang.String)
    Dog{age=0, name='null'}
    Dog{age=10, name='旺财'}
    
    
  • 如果是私有构造怎么办

    私有构造必须先设置暴力权限,然后才能正常使用,否则抛出IllegalAccessException异常
        @Test
        public void test02() throws Exception {
            //使用构造
            //1.反射第一步,先获取Class对象
            Dog dd = new Dog();
            Class clazz = dd.getClass();
            //2.获取单个"public"的构造方法
            Constructor con1 = clazz.getConstructor();
            System.out.println(con1);
    
            Constructor con2 = clazz.getDeclaredConstructor(int.class,String.class);
            System.out.println(con2);
            //3.使用构造创建对象
            Dog dog1 = (Dog)con1.newInstance();
            System.out.println(dog1);
            //私有构造,不能直接使用,因为没有权限
            //设置暴力访问权限
            con2.setAccessible(true);
            Dog dog2 = (Dog)con2.newInstance(10,"旺财");
            System.out.println(dog2);
        }
     输出结果:
        public com.itheima.demo04_GetConstructor.Dog()
        private com.itheima.demo04_GetConstructor.Dog(int,java.lang.String)
        Dog{age=0, name='null'}
        Dog{age=10, name='旺财'}
    
    
8.通过反射获取成员方法&&调用成员方法【重点】
  • 反射获取成员方法

    public Method getMethod(String name,Class...args);获取"public"方法
    public Method getDeclaredMethod(String name,Class...args);获取"任意修饰"方法
        
    public Method[] getMethods(); 获取所有"public"方法,包括父类继承的 
    public Method[] getDeclaredMethods(); 获取所有"任意修饰"方法,不包含父类继承的     
        @Test
        public void test01() throws NoSuchMethodException {
            //1.获取Class对象
            Dog dd = new Dog();
            Class clazz = dd.getClass();
            //2.获取单个"public"成员方法
            Method eat1 = clazz.getMethod("eat");
            System.out.println(eat1);
    
            Method eat2 = clazz.getMethod("eat", String.class, String.class);
            System.out.println(eat2);
            //3.获取单个"任意修饰"成员方法
            Method eat3 = clazz.getDeclaredMethod("eat", String.class);
            System.out.println(eat3);
            System.out.println("=====================");
            //4.获取所有的"public"成员方法,包括父类继承的
            Method[] methods = clazz.getMethods();
            System.out.println(methods.length);
            for (Method method : methods) {
                System.out.println(method);
            }
            System.out.println("=====================");
            //5.获取所有的"任意修饰"的成员方法,但是不包含父类继承的
            Method[] methodss = clazz.getDeclaredMethods();
            System.out.println(methodss.length);
            for (Method method : methodss) {
                System.out.println(method);
            }
        }    
    
    
  • 调用成员方法

    语法格式:
    	成员方法对象.invoke(对象名,参数);
    
    @Test
    public void test02() throws Exception {
        //调用成员方法
        //1.获取Class对象
        Dog dd = new Dog();
        Class clazz = dd.getClass();
        //2.获取单个"public"成员方法
        Method eat1 = clazz.getMethod("eat");
        System.out.println(eat1);
    
        Method eat2 = clazz.getMethod("eat", String.class, String.class);
        System.out.println(eat2);
        //3.使用成员方法
        eat1.invoke(dd);
        eat2.invoke(dd,"shi","niao");
    }
    输出结果:
    public void com.itheima.demo05_GetMethod.Dog.eat()
    public void com.itheima.demo05_GetMethod.Dog.eat(java.lang.String,java.lang.String)
    狗爱吃shi...
    狗爱吃shi,爱喝niao
    
    
  • 如果是私有的成员方法怎么调用呢?

    私有成员方法不能直接调用,必须先设置暴力访问权限,否则抛出IllegalAccessException异常
        
        @Test
        public void test02() throws Exception {
            //调用成员方法
            //1.获取Class对象
            Dog dd = new Dog();
            Class clazz = dd.getClass();
            //2.获取单个"public"成员方法
            Method eat1 = clazz.getMethod("eat");
            System.out.println(eat1);
    
            Method eat2 = clazz.getMethod("eat", String.class, String.class);
            System.out.println(eat2);
    
            Method eat3 = clazz.getDeclaredMethod("eat", String.class);
            //3.使用成员方法
            eat1.invoke(dd);
            eat2.invoke(dd,"shi","niao");
            //私有方法不能直接调用,必须先设置暴力访问权限
            eat3.setAccessible(true);
            eat3.invoke(dd,"shi");
        }
    输出结果
    public void com.itheima.demo05_GetMethod.Dog.eat()
    public void com.itheima.demo05_GetMethod.Dog.eat(java.lang.String,java.lang.String)
    狗爱吃shi...
    狗爱吃shi,爱喝niao
    狗爱吃shi
    
    
9.通过反射获取成员属性【了解,自学】

第二章 注解

1.什么是注解
a.注解是JDK1.5的新特性(注解,增强for,泛型,可变参数)
b.注解相当一种标记,是类的组成部分,可以给类携带一些额外的信息    
c.标记(注解),可以用在各种地方(,,构造方法,普通方法,成员变量,局部变量,...)
d.注解,主要是给编译器或者JVM看的,用于完成某些特定的功能        

2.注解的三个作用
a.给程序带入一些参数 
b.编译检查  
c.给框架使用,作为框架的配置文件    

3.常见的注解介绍
@author:用来标识作者名
@version:用于标识对象的版本号  
@Override: 用于标识该方法是重写的   
@deprecated: 用于标识过期的API
@Test: 用于单元测试的注解    

4.自定义注解(Annotation)【重点】
注意:我们今天自学语法,不学作用
    
自定义类: public class 类名
自定义接口: public interface 接口
自定义枚举: public enum 枚举名
自定义注解: public @interface 注解名
    
格式:
	public @interface 注解名{
        
    }


5.给自定义注解添加属性【重点】
格式:
	public @interface 注解名{
        //注解内部只有属性,没有别的!!
        数据类型 属性名();
        数据类型 属性名() [default 默认值];
    }
注解中并不是所有数据类型都可以的!!!
    只能是以下三大类型:
		a.八大基本类型(byte,short,char,int,long,float,double,boolean)
        b.String,Class,注解类型,枚举类 
        c.以上12具体数据类型的数组  

6.自定义注解练习【重点】
需求:
	 定义一个注解:Book
        包含属性:String value() 书名
        包含属性:double price() 价格,默认值为 100
        包含属性:String[] authors() 多位作者   
/**
 * 自定义的注解
 */
public @interface Book {
    //属性
    String value(); //书名
    double price() default 100.0; //价格
    String[] authors();
}      

7.使用注解时的注意事项【重点】
使用格式:
	@注解名(属性名=属性值,属性名=属性值)
        
@Book(value = "三国演义",price = 150.0,authors = {"罗贯中","杨洋"})
public class Demo {
    @Book(value = "红楼梦",authors = {"曹雪芹"})
    private int age;

    private String name;

    public Demo() {
    }

    @Book(value = "水浒传",authors = "吴承恩")
    public Demo(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public void show(int age){
        String name = "张三";
        System.out.println(age);
    }
}

注意:
	a.使用注解时保证注解的每个属性都必须有值(有默认值我们可以不再赋值,没有默认值我们必须赋值)
    b.如果是数组需要使用{}把值括起来,如果数组的值只有一个,那么大括号可以省略    

8.自定义注解中的特殊属性名value【重点】
a.如果注解中"只有一个属性",并且名字叫做"value",那么使用时可以直接写属性的值,省略属性名
b.如果注解中有value之外的其他属性,那么其他属性都有默认值,
					且使用注解时只给value赋值,那么直接写属性的值,省略属性名.  

/**
 * 特殊属性Value
 */
public @interface Book {
    //属性
    String value(); //书名
    double price() default 100.0;
}

public class Demo {
    private int age;

    private String name;

    public Demo() {
    }
    @Book("三国演义")
    public Demo(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public void show(int age){
        String name = "张三";
        System.out.println(age);
    }
}

9.注解的注解–元注解(了解即可)
a.元注解是Java官方提供的注解
b.元注解用来修饰我们定义的注解(注解的注解)

  • 两个元注解

    元注解常见的有两个:
    	@Target 元注解
            作用:用来标识注解使用的位置,如果没有标识,那么我们的注解在各种地方都可以使用
            取值:必须使用ElementType枚举下的值:
    			TYPE,类,接口 
                FIELD, 成员变量
                METHOD, 成员方法 
                PARAMETER, 方法参数 
                CONSTRUCTOR, 构造方法 
                LOCAL_VARIABLE, 局部变量
        //元注解@Target
        //@Target(ElementType.CONSTRUCTOR)
        //@Target(ElementType.FIELD)
        //@Target(ElementType.LOCAL_VARIABLE)
        
        //使用元注解修饰我们定义的注解            
        @Target({ElementType.FIELD,ElementType.METHOD})
        public @interface MyAnno {
        }
    
    	//使用我们定义的注解
    	@MyAnno
        public class Demo {
            @MyAnno
            private int age;
            @MyAnno
            private String name;
    
            @MyAnno
            public Demo() {
            }
            @MyAnno
            public Demo(@MyAnno int age,@MyAnno String name) {
                this.age = age;
                this.name = name;
            }
            @MyAnno
            public void show(@MyAnno int age){
                @MyAnno
                String name = "张三";
                System.out.println(age);
            }
        }
    
           
        @Retention 元注解    
            作用:用来标识我们注解的生命周期(有效范围) 
            取值:必须是RetentionPolicy枚举中的下面三个值之一
                SOURCE 表示我们的注解只在源码阶段存在,编译成字节码文件后删除
                CLASS 表示我们的注解在源码阶段和字节码阶段存在,加载到内存后删除
                RUNTIME 表示我们的注解在源码阶段,字节码阶段,运行时都存在(永远不删除)  
    
    
10.注解的解析(理解)
  • 什么是注解解析

    通过写代码获取出来某个注解中的那些属性值
    
    
  • 注解解析的步骤

    a.获取注解所在的那个类的Class对象
    b.获取注解所在的对象(可能Field,Method,Constructor) 
    c.判断获取的对象中是否有该注解存在  
    d.如果有我们要的注解,取出我们要的注解即可
    e.从注解中取出属性值即可   
        
    与之相关的API:
    	Annotation: 注解类,Java中所有的注解的父类(了解)
        我们学过的Field,Method,Constructor,Class等类都是实现了AnnotatedElement接口
        public boolean isAnnotationPresent(Class annotationClass);判断是否存在某个注解
        Annotation getAnnotation(Class annotationClass);获取指定类型的注解        
    
    
  • 注解解析代码实现

    a.获取注解所在的那个类的Class对象
    b.获取注解所在的对象(可能Field,Method,Constructor) 
    c.判断获取的对象中是否有该注解存在  
    d.如果有我们要的注解,取出我们要的注解即可
    e.从注解中取出属性值即可
        
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Student {
        int age();
        String name();
        String[] boyFriends();
    }
    
    public class Demo {
    
        @Student(age = 18, name = "小花", boyFriends = {"张三", "李四", "王五"})
        public void show() {
    
        }
    }
    
    public class TestStudent {
        public static void main(String[] args) throws Exception {
    //        a.获取注解所在的那个类的Class对象
            Class clazz = Demo.class;
    //        b.获取注解所在的对象(可能Field,Method,Constructor)
            Method showMethod = clazz.getMethod("show");
    //        c.判断获取的对象中是否有该注解存在
            if (showMethod.isAnnotationPresent(Student.class)) {
                System.out.println("有该注解");
                //        d.如果有我们要的注解,取出我们要的注解即可
                Student anno = showMethod.getAnnotation(Student.class);
                //        e.从注解中取出属性值即可
                int age = anno.age();
                String name = anno.name();
                String[] friends = anno.boyFriends();
                System.out.println("姓名:"+name);
                System.out.println("年龄:"+age);
                System.out.println("男友们:"+ Arrays.toString(friends));
            } else {
                System.out.println("没有该注解");
            }
    
        }
    }    
    
    
11.注解解析案例(理解)
需求:
	模拟Junit的@Test注解

/**
 * 自定义的注解,用于模拟@Test注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTest {

}
 
public class Demo {
    @MyTest
    public void test01(){
        System.out.println("11111");
    }

    @MyTest
    public void test02(){
        System.out.println("22222");
    }

    @MyTest
    public void test03(){
        System.out.println("33333");
    }

    @MyTest
    public void test04(){
        System.out.println("44444");
    }
}

public class TestMyTestDemo {
    public static void main(String[] args) throws Exception {
        //当这个main方法执行时,要求Demo类中有@MyTest注解的方法会执行
        //没有@MyTest注解的方法不会执行
        //思考:步骤
        //1.获取@MyTest注解所在的类
        Class clazz =  Demo.class;
        //2.获取所有方法
        Method[] methods = clazz.getMethods();
        //3.循环
        for (Method method : methods) {
            //4.判断
            if (method.isAnnotationPresent(MyTest.class)) {
                //有该注解
                method.invoke(new Demo());
            }else{
                //没有该注解
            }
        }
    }
}

总结
能够通过反射技术获取Class字节码对象。【重点】
    Class clazz1 = 类名.class
    Class clazz2 = 对象名.getClass();
	Class clazz3 = Class.forName("包名.类名");
能够通过反射技术获取构造方法对象,并创建对象。【重点】
    获取构造:
	public Constructor getConstructor(参数的类型.class,...);//获取单个"public"构造
	public Constructor getDeclaredConstructor(参数的类型.class,...);//获取单个"任意修饰"构造

	public Constructor[] getConstructors();//获取所有"public"构造【了解】
	public Constructor[] getDeclaredConstructors();//获取所有"任意修饰"构造【了解】

	使用构造:
		构造方法对象.newInstance(实际参数);
		如果是私有构造: 必须在使用之前设置暴力权限
        构造方法对象.setAccessible(true);     
	
能够通过反射获取成员方法对象,并且调用方法 【重点】
    获取方法:
	public Method getMethod(String name,参数的类型.class,...);//获取单个"public"方法
	public Method getDeclaredMethod(String name,参数的类型.class,...);//获取单个"任意修饰"方法

	public Method[] getMethods();//获取所有"public"方法,包含父类继承的【了解】
	public Method[] getDeclaredMethods();//获取所有"任意修饰"方法,不包含父类的【了解】	

	使用方法:
		成员方法对象.invoke(对象名,方法的实际参数);
		如果该方法是私有的,那么必须在使用之前设置暴力权限
        成员方法对象.setAccessible(true);     
    
能够通过反射获取属性对象,并且能够给对象的属性赋值和取值【了解,有兴趣自学】
    
能够说出注解的作用
    a。给程序带入参数  b。编译检查	c。给框架做配置文件
能够自定义注解和使用注解【重点】
    自定义注解:
    	public @interface 注解名{
    		数据类型 属性名();
    		数据类型 属性名() default 默认值;
    		数据类型[] 属性名() default {默认值1,默认值2,...}
		}
		这里的数据类型,只能是三大类(a.8大基本类型 b.四大引用类型 c.以上12中的数组)
    特殊的属性:value(怎么特殊???)        
能够说出常用的元注解及其作用 【理解】
     @Target
     @Retension   
能够解析注解并获取注解中的数据 【理解】
     写代码把某个注解上的那些属性值获取打印出来
     a.获取注解所在的那个类的Class对象
     b.获取注解所在的对象(可能Field,Method,Constructor) 
     c.判断获取的对象中是否有该注解存在  
     d.如果有我们要的注解,取出我们要的注解即可
     e.从注解中取出属性值即可     
能够完成注解的MyTest案例 【理解】
     本质上也是注解的解析,只是没有获取注解的属性值,而是调用含有该注解的方法   

发布了23 篇原创文章 · 获赞 0 · 访问量 1130

猜你喜欢

转载自blog.csdn.net/qq_44845814/article/details/105212412