反射、注解、动态代理、JDK8新特性

day14【反射、注解、动态代理、JDK8新特性】

今日内容:
	"反射:以后我们学的所有框架底层都是由反射
    "注解:今天只学基本语法
    设计模式:  动态代理设计模式(基本格式)
    JDK8的新特性         

第一章.反射

1.类的加载
源文件--通过javac编译-->字节码文件---通过Java命令(通过ClassLoader)--->JVM运行
 
字节码文件什么时候会被加载?
	当该类被使用到时就会被加载
字节码文件需要加载几次?       
    只需要加载一次,当前第一次使用该类时加载,以后使用到该类不需要加载
"字节码文件被加载JVM方法区内存之后,JVM会干什么事?"
    JVM会在堆中为该字节码文件创建一个Class对象(字节码文件对象),对象中保存了字节码文件中所有的信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v7rCBuSz-1578312743688)(img/image-20200105091214510.png)]

2.什么是反射
反射是一种运行时技术,运行时获取某个类的Class对象,从而解剖它,从中获取成员变量,成员方法,构造方法等,进而可以使用他们(无论私有与否)
3.反射在实际开发中的应用
a.编写IDE(集成开发环境),比如IDEA,Eclipse
b.以后我们底层学习框架和设计框架都必须使用反射技术        
4.反射中万物皆对象的概念
反射中万物皆对象:
	字节码文件 ---> Class对象
    成员变量 ---> Field对象
    成员方法 ---> Method对象
    构造方法 ---> Constructor对象   
    newInstance ---> 创建对象
    invoke ---> 调用/执行

体验一下反射中语法:
	正常语法					反射语法
    new 构造方法(参数);		   构造方法对象.newInstance(参数);
	对象名.成员方法名(参数);		成员方法对象.invoke(对象名,参数);

    sout(对象名.成员变量名);	 sout(成员变量对象.get(对象名)); 
	对象名.成员变量名 = 值    	成员变量对象.set(对象名,);
    

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kTKj6BJM-1578312743689)(img/image-20200105092106340.png)]

5.反射的第一步获取字节码文件对象(Class对象)
Java提供三种方式,可以获取到Class对象
/**
 * 获取Class对象
 */
public class TestDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.通过类的静态成员来获取
        Class clazz1 = Cat.class;
        System.out.println(clazz1);
        //2.通过该类的某个对象,来获取Class对象
        Cat cc = new Cat();
        Class clazz2 = cc.getClass();
        System.out.println(clazz2);
        //3.通过Class类的静态方法
        Class clazz3 = Class.forName("com.itheima.demo02_GetClassObject.Cat");
        System.out.println(clazz3);
        //注意:以上三种是Java提供的三种方式,而不是获取三个Class对象
        System.out.println(clazz1 == clazz2);
        System.out.println(clazz1 == clazz3);
        System.out.println(clazz2 == clazz3);
        //以上三个Class对象,实际上是同一个Class对象,只是获取的方式不同而已
    }
}   
6.Class对象中的三个常见方法
public String getName(); 获取全限定类名(包名.类名)
public String getSimpleName(); 获取类名(不带包名)
public Object newInstance();  创建该Class对象所代表类的对象  
    
/**
 * Class对象的三个方法
 */
public class TestDemo {
    public static void main(String[] args) throws Exception {
        //1.通过类的静态成员来获取
        Class clazz1 = Cat.class;
        //4.获取全限定类名
        System.out.println("---------------");
        System.out.println(clazz1.getName());
        //5.获取类名
        System.out.println(clazz1.getSimpleName());
        //6.创建对象(Class所代表类Cat的对象)
        Object obj = clazz1.newInstance();
        System.out.println(obj);
    }
}   
7.通过反射获取构造方法&&使用构造方法创建对象
获取构造:
	public Constructor getConstructor(构造方法的参数类型.class...);只能获取"public"构造
    public Constructor getDeclaredConstructor(构造方法的参数类型.class...);获取"任意"构造    

使用构造:
	public Object newInstace(构造方法的实际参数);
如果是私有构造怎么使用?
    a.先设置该构造方法具有暴力访问权限
    	构造方法对象.setAccessible(true);
	b.然后正常使用
        public Object newInstace(构造方法的实际参数);

/**
 * 通过反射获取其中的构造方法,并使用构造方法
 */
public class TestDemo {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1.获取Class对象
        Class cc = Cat.class;
        //2.获取构造方法对象
        Constructor con1 = cc.getConstructor();
        System.out.println(con1);
        Constructor con2 = cc.getConstructor(int.class, String.class);
        System.out.println(con2);
        //如果是非公有构造,必须调用getDeclaredConstructor来获取
        Constructor con3 = cc.getDeclaredConstructor(String.class);
        System.out.println(con3);
        //3.通过构造方法对象就可以创建对象
        System.out.println("---------");
        Object obj1 = con1.newInstance();
        System.out.println(obj1);
        Object obj2 = con2.newInstance(10, "肥猫");
        System.out.println(obj2);
        //如果是私有构造方法,那么必须先设置暴力访问权限
        con3.setAccessible(true);
        //然后才能正常使用
        Object obj3 = con3.newInstance("加肥猫");
        System.out.println(obj3);
    }
}
8.通过反射获取成员方法&&调用成员方法
获取成员方法:
	public Method getMethod("方法名",方法参数类型.class...); 获取"public"修饰成员方法
    public Method getDeclaredMethod("方法名",方法参数类型.class...); 获取"任意"修饰成员方法   
使用成员方法:
	public Object invoke(对象名,执行方法需要的实际参数); -- 执行该成员方法对象
如果是私有的成员方法怎么调用呢?
    a.先设置暴力访问权限
        成员方法对象.setAccessible(true);
	b.正常调用该方法即可
        public Object invoke(对象名,执行方法需要的实际参数); -- 执行该成员方法对象	
        
/**
 * 通过反射获取成员变量,并使用成员方法
 */
public class TestDemo {
    public static void main(String[] args) throws Exception {
        //1.获取Class对象
        Class cc = Class.forName("com.itheima.demo04_GetMethod.Cat");
        //2.获取成员方法
        Method m1 = cc.getMethod("eat", String.class);
        System.out.println(m1);

        Method m2 = cc.getMethod("eat", String.class, String.class);
        System.out.println(m2);
        //如果成员方法是非公有的,必须通过getDeclaredMethod方法获取
        Method m3 = cc.getDeclaredMethod("eat");
        System.out.println(m3);

        //3.使用成员方法对象
        Cat cat = new Cat(10,"加菲猫");
        m1.invoke(cat,"鱼"); // 相当于 cat.eat("鱼")

        m2.invoke(cat,"鱼","啤酒");// 相当于cat.eat(鱼","啤酒");

        //如果是私有方法,必须先设置暴力访问权限
        m3.setAccessible(true);
        //然后调用正常调用方法
        m3.invoke(cat); //相当于cat.eat();
    }
}              
9.通过反射获取成员属性(了解即可)
因为成员变量都是私有的,并且有get/set方法,我们通过反射获取get/set方法就可以了!!
    
获取成员变量:
	public Field getField(String 成员变量名); -- 获取"public"成员变量
	public Field getDeclaredField(String 成员变量名); -- 获取"任意"成员变量
使用成员变量:  
	"取出成员变量的值
	成员变量对象.get(对象名); // 相当于 对象名.成员变量名
	需要先设置暴力权限
    成员变量对象.setAccessible(true);
	成员变量对象.get(对象名);
	"修改成员变量的值
    成员变量对象.set(对象名,新的值);
	需要先设置暴力权限
    成员变量对象.setAccessible(true);
	成员变量对象.set(对象名,新的值);
10.反射中的其他方法(了解即可)
获取构造:
	public Constructor[] getConstructors(); -- 获取所有"public"构造
	public Constructor[] getDeclaredConstructors(); -- 获取所有"任意"构造
获取方法:
	public Method[] getMethods(); -- 获取所有"public"成员方法,包括父类继承的
    public Method[] getDeclaredMethods(); -- 获取所有"任意"成员方法(不包括父类继承的)   
        
获取成员变量:
	public Filed[] getFields();  -- 获取所有"public"的成员变量       
	public Filed[] getDeclaredFields();  -- 获取所有"任意"的成员变量  
        

第二章 注解(基本语法)

1.JDK1.5新特性–注解
什么是注释:
	对代码进行解释说明的文字(给程序员看)
什么是注解:
	对代码进行解释说明的语法(给JVM看的,程序员也可以看)
2.注解的两个作用
a.给程序带入参数(框架的时候使用)
b.编译检查(在编译时期,检查我们的语法是否符合程序的要求)
    @Override 方法重写注解
    @FunctionalInterface 函数式接口注解
    @Deprecated 方法过期注解
c.作为框架的配置文件(框架的时候使用)  
3.常用两个注解介绍
@author 给Java文件,标记作者
@version 给Java文件,标记版本号
@Override 方法重写注解
@FunctionalInterface 函数式接口注解
@Deprecated 方法过期注解
@Test 单元测试Junit的注解    
4.自定义注解
自定义类: public class 类名
自定义接口: public interface 接口名
自定义注解: public @interface 注解名
    
格式:
	public @interface 注解名{
        
    }
5.给自定义注解添加属性
格式:
	public @interface 注解名{
        //注解中只能写属性,格式: 数据类型 属性名();
        数据类型 属性名() [default 默认值]; 
        数据类型 属性名() [default 默认值];
        数据类型 属性名() [default 默认值];
    }
注解中属性的数据类型也是有限制:
	a.八大基本类型
    b.引用类型中只能写:String,枚举,其他注解,Class
    c.以上12种类型的一维数组             
6.自定义注解练习
/**
 * 自定义注解
 */
public @interface MyAnnotation {
    //注解的属性
    int age(); // default 18;
    String name();// default "张三";
    String[] hobbies();// default {"抽烟", "喝酒", "跳楼"};
}

/**
 * 使用注解格式:
 * 	@注解名(属性名=属性值,属性名=属性值,..)
 *  如果注解中某个属性没有默认值,那么使用该注解时必须给属性赋值,如果有默认值,可以赋值也可以不赋值    
 */      
@MyAnnotation(age = 18,name = "旺财",hobbies = {"抽烟", "喝酒", "跳楼"})
public class Demo {
    @MyAnnotation(age = 18,name = "旺财",hobbies = {"抽烟", "喝酒", "跳楼"})
    private String job;

    @MyAnnotation(age = 18,name = "旺财",hobbies = {"抽烟", "喝酒", "跳楼"})
    public Demo(String job) {
        this.job = job;
    }

    @MyAnnotation(age = 18,name = "旺财",hobbies = {"抽烟", "喝酒", "跳楼"})
    public void show(@MyAnnotation(age = 18,name = "旺财",hobbies = {"抽烟", "喝酒", "跳楼"}) int age) {
        @MyAnnotation(age = 18,name = "旺财",hobbies = {"抽烟", "喝酒", "跳楼"})
        String name = "";
    }
}        
7.使用注解时的注意事项
a.如果注解中某个属性没有默认值,那么使用该注解时必须给属性赋值,如果有默认值,可以赋值也可以不赋值
b.我们现在定义的注解,可以修饰基本上任意的地方(类上,方法上,局部变量上等...)
c.我们的注解没有实际含义,注解想要有实际含义必须有注解解析器的支持    
8.自定义注解中的特殊属性名value
a.如果注解中只有一个属性,且属性名为value,那么使用该注解时,可以省略value直接给value赋值即可
/**
 * 特殊的属性value
 */
public @interface MyAnno {
    String value();
}  
@MyAnno("abc") -- 此处省略value的名,直接写value的值
public class Demo {
}
b.如果注解中有多个属性,但是有一个叫value,其他属性都有默认值,使用时可以可以省略value直接给value赋值即可
/**
 * 特殊的属性value
 */
public @interface MyAnno {
    String value();
    int age() default 20;
}
@MyAnno("abc") -- 此处省略value的名,直接写value的值,age不需要赋值
public class Demo {
}   
9.注解的注解–元注解
元注解: 修饰注解的注解,称为元注解
  • 两个元注解

    @Target 元注解,修饰普通的注解
        其作用是,规定普通的注解使用的目标:具体的使用目标,可以是以下几种
            ElementType.TYPE  表示作用目标必须是类上或者接口上
            ElementType.FIELD 表示作用目标必须是成员变量上
            ElementType.METHOD	表示作用目标必须是成员方法上
            ElementType.PARAMETER 表示作用目标必须是方法参数上
            ElementType.CONSTRUCTOR 表示作用目标必须是构造方法上
            ElementType.LOCAL_VARIABLE	表示作用目标必须是局部变量上     
    /**
     * 使用@Target元注解,修饰普通的注解
     * 作用:规定普通注解使用的目标
     */
    //@Target(ElementType.TYPE) 表示作用目标必须是类上或者接口上
    //@Target(ElementType.FIELD) 表示作用目标必须是成员变量上
    //@Target(ElementType.METHOD) 表示作用目标必须是成员方法上
    //@Target(ElementType.CONSTRUCTOR) 表示作用目标必须是构造方法上
    //@Target(ElementType.PARAMETER) 表示作用目标必须是方法参数上
    //@Target(ElementType.LOCAL_VARIABLE) 表示作用目标必须是局部变量上
    @Target({ElementType.FIELD,ElementType.METHOD}) 表示作用目标必须是成员变量和成员方法上
    public @interface Anno1 {
    }
    		
    @Retension 元注解,修饰普通的注解
        其作用是,规定普通注解的生命周期,具体的使用生命周期,可以是以下几种
    		    RetentionPolicy.SOURCE 表示我们的注解只在源码阶段存在,编译成字节码后删除
        		RetentionPolicy.CLASS 表示我们的注解在源码和字节码阶段都存在,运行时删除
        		RetentionPolicy.RUNTIME 表示我们的注解一直存在
        
    /**
     * 使用@Retension元注解,修饰普通的注解
     * 作用:规定普通注解的生命周期
     */ 
    //@Retention(RetentionPolicy.SOURCE) 表示我们的注解只在源码阶段存在,编译成字节码后删除
    //@Retention(RetentionPolicy.CLASS) 表示我们的注解在源码和字节码阶段都存在,运行时删除
    @Retention(RetentionPolicy.RUNTIME) 表示我们的注解一直存在
    public @interface Anno2 {
    }
    
10.注解的解析
就是通过代码,读取某个位置上注解的属性值
    
注解解析的固定步骤:
	a.获取注解所在的Class对象
    b.获取注解所在的具体目标(Field,Method,Constructor)
    c.判断目标是否真的有此注解
    d.从目标上获取我们要的注解
    e.从注解上获取其各个属性值   
        
/**
 * 自定义注解:书注解
 */
@Retention(RetentionPolicy.RUNTIME) -- 设置Book注解的生命周期是一直存在
public @interface Book {
    String name();
    double price();
    int pages();
    String[] authors();
}        

public class Pig {
    private int age;

    public Pig(int age) {
        this.age = age;
    }

    @Book(name = "三国演义",price = 199.99,pages = 1001,authors = {"曹雪芹","罗贯中","吴承恩","施耐庵"})
    public void eat() {

    }
}

public class TestDemo {
    public static void main(String[] args) throws NoSuchMethodException {
//        注解解析的固定步骤:
//        a.获取注解所在的Class对象
        Class pigClass = Pig.class;
//        b.获取注解所在的具体目标(Field,Method,Constructor)
        Method eatMethod = pigClass.getMethod("eat");
//        c.判断目标是否真的有此注解
        boolean b = eatMethod.isAnnotationPresent(Book.class);
        if (b) {
//        d.从目标上获取我们要的注解
            Book book = eatMethod.getAnnotation(Book.class);
//        e.从注解上获取其各个属性值
            System.out.println("书名:" + book.name());
            System.out.println("价格:" + book.price());
            System.out.println("页数:" + book.pages());
            System.out.println("作者们:" + Arrays.toString(book.authors()));
        } else {
            System.out.println("该方法上没有此注解");
        }
    }
}
11.综合练习_模拟Junit的@Test注解
需求: 编写一个注解,模拟Junit的@Test注解功能(使用main方法来代替右键执行)
   
/**
 * 自定义注解,模拟Junit的@Test注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
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 TestDemo {
    public static void main(String[] args) throws Exception {
        //1.获取目标类的字节码文件对象
        Class clazz = Demo.class;
        //2.获取目标类上所有的成员方法
        Method[] methods = clazz.getMethods();
        //3.遍历判断
        for (Method method : methods) {
            //4.判断该方法上是否有注解
            if (method.isAnnotationPresent(MyTest.class)) {
                //5.执行该方法
                method.invoke(new Demo());
            }
        }
    }
}

第三章 动态代理

1.代理模式介绍
代理设计模式:
	被代理对象,没有能力或者不愿意完成某件事情,找有一个代理对象,由代理对象去完成
    作用:就是对被代理对象的功能进行增强    
2.动态代理概述
底层通过反射技术,动态地(在运行时,需要时)生成被代理对象的代理对象
3.案例引出
/**
 * 学校提供的教师服务
 */
public interface SchoolService {
    String login(String loginName, String passWord);
    String getAllClazzs();
}

/**
 * 学校提供的教师服务的实现类
 */
public class SchoolServiceImpl implements SchoolService {
    /**
     * 登录服务
     */
    @Override
    public String login(String loginName, String passWord) {
        try {
            Thread.sleep(new Random().nextInt(5000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "恭喜您,登录成功...";
    }

    /**
     * 查询班级服务
     */
    @Override
    public String getAllClazzs() {
        try {
            Thread.sleep(new Random().nextInt(5000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "1班,2班,3班";
    }
}

public class TestDemo {
    public static void main(String[] args) {
        //1.创建一个实现类对象
        SchoolServiceImpl service = new SchoolServiceImpl();
        //2.登录
        String result = service.login("laowang123", "gebi123");
        System.out.println(result);
        //3.查询班级
        String allClazzs = service.getAllClazzs();
        System.out.println(allClazzs);
    }
}

4.使用动态代理优化代码
public class TestDemo {
    public static void main(String[] args) {
      //1.为学校的服务实现类,创建一个动态代理
        /**
         * Java提供创建代理对象的方法
         * Proxy.newProxyInstance(
         *      参数1:当前类的类加载器 基本上固定: 当前类.class.getClassLoader()
         *      参数2:被代理对象所有实现的接口字节码对象数组 new Class[]{接口1.class,接口2.class..}
         *      参数3:处理接口的实现类对象(处理类对象)
         * );
         */
        SchoolService serviceProxy = (SchoolService)Proxy.newProxyInstance(TestDemo.class.getClassLoader(), new Class[]{SchoolService.class}, new InvocationHandler() {

            /**
             * 当我们调用动态代理对象的方法时,动态代理对象,会通过反射,把调用的方法,以及方法的参数封装起来
             * @param proxy 其实就是代理对象
             * @param method 通过代理对象调用的方法
             * @param args 通过带俩对象调用方法时传入的参数
             * @return
             * @throws Throwable
             */
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                long start = System.currentTimeMillis();
                Object result = method.invoke(new SchoolServiceImpl(),args);
                long end = System.currentTimeMillis();
                System.out.println("耗时:"+(end-start)+"毫秒");
                return result;
            }
        });
        //2.调用动态代理对象的方法
//        String result = serviceProxy.login("1111", "2222");
//        System.out.println(result);
//
        String allClazzs = serviceProxy.getAllClazzs();
        System.out.println(allClazzs);
    }
}

第四章 JDK8新特性

Lambda
Stream    
1. 方法引用
  • 方法引用介绍

    允许开发者可以直接引用现有的方法,来代理函数式接口的匿名内部类(Lambda) 
    
  • 方法引用四种方式

    a.通过对象名,引用对象中成员方法
        	对象名::成员方法名
    b.通过类名,直接引用类中的静态方法
            类名::静态方法名
    c.引用类的构造器
            类名::new
    d.引用数组的构造器
           数据类型[]::new  
    
  • 基于静态方法引用的代码演示

    public class TestDemo {
        public static void main(String[] args) {
            //调用方法
    //        method(new 接口的实现类对象());
    //        method(new Consumer<String>() {
    //            @Override
    //            public void accept(String s) {
    //                System.out.println(s);
    //            }
    //        });
    //        method(s -> System.out.println(s));
            //使用方法引用
            method(System.out::println);
            
    //回顾:
            Stream<String> stream = Stream.of("jack", "rose", "tom", "jerry");
    //        stream.forEach(new Consumer<String>() {
    //            @Override
    //            public void accept(String s) {
    //                System.out.println(s);
    //            }
    //        });
    //        stream.forEach(s -> System.out.println(s));
    
            stream.forEach(System.out::println);
    
        }
    
        public static void method(Consumer<String> con) {
            con.accept("java");
        }
    }
    
2. Base64
  • Base64介绍

    一种编码技术
    
  • Base64内嵌类和方法描述

    Base64.EnCoder和Base64.DeCoder
    Base64.MimeEnCoder和Base64.MimeDeCoder   
    Base64.URLEnCoder和Base64.URLDeCoder    
    
  • Base64代码演示

    
    public class Base64Demo {
        public static void main(String[] args) {
            //1.EnCoder和DeCoder
            Base64.Encoder encoder = Base64.getEncoder();
            String encodeToString = encoder.encodeToString("中国HelloWorld我爱你".getBytes());
            System.out.println("编码之后的字符串:"+encodeToString);
    
            Base64.Decoder decoder = Base64.getDecoder();
            byte[] bs = decoder.decode(encodeToString);
            System.out.println("解码之后的字符串:" + new String(bs));
            System.out.println("--------------");
            //2.MIME类型编码和解码
            String encodeToString1 = Base64.getMimeEncoder(4,"-".getBytes()).encodeToString("中国HelloWorld我爱你".getBytes());
            System.out.println("编码之后的字符串:"+encodeToString1);
    
            byte[] bs1 = Base64.getMimeDecoder().decode(encodeToString1);
            System.out.println("解码之后的字符串:" + new String(bs1));
    
            //3.URLEnCoder和URLDeCoder
            System.out.println("--------------");
            byte[] bs2 = Base64.getUrlEncoder().encode("http://www.itheima.com".getBytes());
            System.out.println("编码之后的字符串:"+new String(bs2));
    
            byte[] bytes = Base64.getUrlDecoder().decode(bs2);
            System.out.println("解码之后的字符串:"+new String(bytes));
    
            //UUID 生成一个32个长度的全球唯一字符串
            UUID uuid = UUID.randomUUID();
            System.out.println(uuid);
        }
    }
    
总结
"能够通过反射技术获取Class字节码对象
"能够通过反射技术获取构造方法对象,并创建对象。
"能够通过反射获取成员方法对象,并且调用方法。
能够通过反射获取属性对象,并且能够给对象的属性赋值和取值。
"能够说出注解的作用
    编译检查
"能够自定义注解和使用注解
    public @interface 注解名{
    	数据类型 属性名();
    	数据类型只能是三大类: 基本类型,四个引用类型(String),以上12个一维数组
	}

	@注解名(属性名="具体的值",....)
    特殊value
        
能够说出常用的元注解及其作用
        @Target
        @Rentetion
"能够解析注解并获取注解中的数据
        a.获取Class
        b.获取具体对象(Method,Constructor,Field)
        c.判断 isAnnotaionPresent(注解名.class);
		d.取出 getAnnotaion(注解名.class);
		e.获取注解中的属性值 注解对象.属性名();
能够完成注解的MyTest案例
    
能够说出动态代理模式的作用
能够使用Proxy的方法生成代理对象  
能够使用四种方法的引用(建议观看课后扩展视频_方法引用完整版)
能够使用Base64对基本数据、URL和MIME类型进行编解码

猜你喜欢

转载自blog.csdn.net/qq_41371264/article/details/103864122