【230天】黑马程序员27天视频学习笔记【Day27-下】

ellende 2019-06-25

叨叨两句

  1. 今天学习内容好多,忙晕了,明天填坑
  2. 今天学习时间没安排好,虽然整体学习效果不错,但是今天的任务完成的不好,不加分,算做小小的惩罚
  3. 坑没填完,继续填(2017.9.24)

27-04: 反射Class.forName()读取配置文件

package test;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;

public class Demo_reflect {
    public static void main(String[] args) throws Exception {
        Juicer j = new Juicer();
        BufferedReader br = new BufferedReader(new FileReader("config.properties")); 
        Class clazz = Class.forName(br.readLine());
        Fruit f = (Fruit)clazz.newInstance();
        j.run(f);
    }
    
}

interface Fruit {
    public void squeeze();
}

class Juicer {
    public void run(Fruit f) {
        f.squeeze();
    }
}

class Apple implements Fruit {
    public void squeeze() {
        System.out.println("苹果汁好了");
    }
}

class Orange implements Fruit {
    public void squeeze() {
        System.out.println("橘子汁好了");
    }
}

27-05: 通过过反射获取带参构造方法并使用

关注点

  1. Class类的newInstance方法是使用该类的无参构造函数创建对象,如果一个类没有无参构造函数,就不能这样创建了,需要使用Constructor类的newInstance方法。

思路

  1. 拿到Person类的字节码文件
  2. 使用getConstructor方法获取有参构造方法【该方法也可以获取无参构造方法,参数列表不传参即可】
  3. 使用Constructor类的newInstance方法创建对象【注意,不是Class类的方法】
package com.test;

import java.io.BufferedReader;
import java.io.FileReader;
import java.lang.reflect.Constructor;

public class Demo_reflect_1 {
    //拿到Person类的有参构造函数并创建对象
    public static void main(String[] args) throws Exception {
        //拿到Person的字节码文件
        BufferedReader br = new BufferedReader(new FileReader("config.properties"));
        Class clazz = Class.forName(br.readLine());
        //获取Person的有参构造函数
        Constructor c = clazz.getConstructor(String.class,int.class);
        //创建Person对象【有参】
        Person p = (Person)c.newInstance("张三",23);
        //打印
        System.out.println(p);
        
    }

}

27-06:通过反射获取成员变量并使用

关注点

  1. 针对非私有的成员变量

    1. 使用Class类中的getField方法即可获取指定字段【返回Field对象】
    2. 使用已传入特定字段的Field对象中的set方法可以将传入的Person对象的指定字段修改为指定的值。
  2. 针对私有的成员变量

    1. 使用Class类中的getDeclaredField方法即可获取指定字段【返回Field对象】
    2. 使用已传入特定字段的Field对象中的setAccessible(true)方法可以去除私有权限
    3. 使用已传入特定字段的Field对象中的set方法可以将传入的Person对象的指定字段修改为指定的值。
package com.test;

import java.io.BufferedReader;
import java.io.FileReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class Demo_reflect_2 {
    /**
     * @param args
     * 通过反射的方式获得成员变量并修改
     * @throws Exception 
     * @throws ClassNotFoundException 
     */
    public static void main(String[] args) throws ClassNotFoundException, Exception {
        //通过IO流读取配置文件
        BufferedReader br = new BufferedReader(new FileReader("config.properties"));
        //获取字节码文件
        Class clazz = Class.forName(br.readLine());
        //通过获取的字节码文件获取有参构造函数
        Constructor c = clazz.getConstructor(String.class,int.class);
        //通过获取的有参构造新建一个完成初始化的Person对象
        Person p = (Person)c.newInstance("张三",23);

        //1.属性非私有的情况
        
//            //获取字节码文件中的指定字段name
//            Field f = clazz.getField("name");
//            //通过获取的指定字段修改Person对象的name属性
//            f.set(p,"李四");
//            
        
        //2.私有的情况【使用暴力反射】
        
            //获取字节码文件中的指定字段name
            Field f = clazz.getDeclaredField("name"); 
            //去除私有权限
            f.setAccessible(true);
            //通过获取的指定字段修改Person对象的name属性
            f.set(p,"李四");
            
        //打印测试
        System.out.println(p);
    }
}

27-07:通过反射获取方法并使用

主要内容

  1. 获取无参的方法

    1. 使用Class类的getMethod(方法名)方法获取字节码文件中的方【返回Method对象】
    2. 使用Method类的方法invoke(指定对象)运行获取的方法。
  2. 获取有参的方法

    1. 使用Class类的getMethod(方法,参数类型的字节码文件)方法获取字节码文件中的方【返回Method对象】
    2. 使用Method类的方法invoke(指定对象,获取的方法的参数)运行获取的方法。

其它内容

  1. 对于私有方法,获取并使用的方式和获取私有成员变量的方式差不多。
package com.test;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * @author XPS
 * 通过反射获取方法并使用
 */
public class Demo_reflect_3 {
    public static void main(String[] args) throws Exception, Exception {
        //获取字节码文件
        BufferedReader br = new BufferedReader(new FileReader("config.properties"));
        Class clazz = Class.forName(br.readLine());
        //通过获取的字节码文件获取有参构造函数
        Constructor c = clazz.getConstructor(String.class,int.class);
        //新建一个初始化的Person对象p
        Person p = (Person)c.newInstance("张三",23);
        
//        //1. 获取无参方法
//            //通过反射获得这个字节码中的方法eat
//            Method m = clazz.getMethod("eat");
//            //运行p的eat方法
//            m.invoke(p);
        //2. 获取有参方法
            //通过反射获得这个字节码中的方法eat(int num)
            Method m = clazz.getMethod("eat",int.class);
            //运行p的eat方法
            m.invoke(p,2);
    }
    
    
    
}

27-08:通过反射越过泛型检查

原理

泛型只在编译期有效,运行期将被擦除,而反射是在运行期获取字节码文件来调用方法添加我们想要添加的字符串

package com.test;

import java.lang.reflect.Method;
import java.util.ArrayList;

/**
 * @author XPS
 * 使用反射技术绕过泛型检查,实现往ArrayList<Integer>中添加字符串"abc"
 * 
 * 原理: 泛型只在编译期有效,运行期将被擦除,而反射是在运行期获取字节码文件来调用方法添加我们想要添加的字符串
 * 
 */
public class Demo_reflect_4 {

    public static void main(String[] args) throws Exception {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(111);
        list.add(222);
        //list.add("abc");  //这个地方填加字符串会报错
        
        Class clazz = Class.forName("java.util.ArrayList");
        Method m = clazz.getMethod("add",Object.class);
        m.invoke(list, "abc");
        
        System.out.println(list);
        
    }

}

27-09:使用反射改变指定对象特定属性为特定值

package com.test;

public class Demo_reflect_5 {
    public static void main(String[] args) throws Exception {
        Student stu = new Student("张三",23);
        Tool.setProperty(stu, "name", "李四");
        System.out.println(stu);
    }
}


class Student {
    private String name;
    private int age;
    
    public Student() {
        super();
        // TODO Auto-generated constructor stub
    }
    
    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    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;
    }

    @Override
    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }
    
    
}
package com.test;

import java.lang.reflect.Field;

public class Tool {
    public static void setProperty(Object obj, String propertyName, Object value) throws Exception, SecurityException {
        Class clazz = obj.getClass();
        Field f = clazz.getDeclaredField(propertyName);
        f.setAccessible(true);
        f.set(obj, value);
    }
}

27-10:反射练习

package com.test;

import java.io.BufferedReader;
import java.io.FileReader;

import cn.itcast.heima.DemoClass;

public class Demo_reflect_6 {

    /**
     * @param args
     * 
     * 已知一个类,定义如下:
     * package cn.itcast.heima;
        public class DemoClass { public void run() { System.out.println("welcome to heima!"); } }
        (1) 写一个Properties格式的配置文件,配置类的完整名称。
        (2) 写一个程序,读取这个Properties配置文件,获得类的完整名称并加载这个类,用反射的方式运行run方法。
     * @throws Exception 
     * @throws ClassNotFoundException 
     * 
     */
    
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new FileReader("XXX.properties"));
        Class clazz = Class.forName(br.readLine());
        DemoClass d = (DemoClass)clazz.newInstance();
        d.run();
    }

}
package cn.itcast.heima;

public class DemoClass {
    public void run() {
        System.out.println("Welcome to heima!");
    }
}

27-11:动态代理的概述和实现

留坑待填

27-12:设计模式(模板(Template)设计模式概述和使用)

模板设计模式

定义一个算法的骨架,而将具体的算法延迟到子类中实现

优点

使用模板方法模式在定义算法骨架的同时,可以灵活的实现具体算法,满足用户灵活多变的需求。

缺点

如果算法骨架有修改,需要修改抽象类

使用模板设计模式前

package com.test;

public class Demo_reflect_9 {

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for(int i = 0; i < 100000; i++) {
            System.out.println("x");
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }

}

使用模板设计模式后

package com.test;

public class Demo_reflect_9 {
    public static void main(String[] args) {
        Demo d = new Demo();
        System.out.println(d.getTime());
    }
}

abstract class GetTime {
    public final long getTime() {
        long start = System.currentTimeMillis();
        code();
        long end = System.currentTimeMillis();
        return end - start;
    }
    
    public abstract void code();
}


class Demo extends GetTime {

    @Override
    public void code() {
        for(int i = 0; i < 100000; i++) {
            System.out.println("x");
        }
    }
    
}

我的理解

  1. 一个抽象类中,写一个final修饰的必须要执行的方法,再写一个可让子类随便定义的抽象方法。

27-(13-16):JDK5新特性

自己实现枚举类

通过enum实现枚举类

枚举注意事项

枚举类常见方法

27-17:JDK7的六个新特性回顾和讲解

27-18:JDK8新特性

相关推荐