Java类加载器和反射

1. 类的加载

当程序要使用某个类的时候,如果该类还没有被加载到内存中,则系统会通过加载连接,初始化三个步骤来实现对这个类的初始化

加载:将Class文件读入内存,并创建一个class对象,任何类被使用时系统都会建立一个Class对象

连接:验证:是否有正确的内部结构,并和其它类协调一致

准备:负责为类的静态成员分配内存,并设置为默认初始化值

解析:将类的二进制数据中的符号引用替换为直接引用

类初始化时机:

1. 方法类的静态变量,或者为静态变量赋值

2. 调用类的静态方法

3. 创建类的实例

4. 使用反射的方式来强制创建某个类或者接口对应的java.lang.Class对象

5. 初始化某个类的对象

6. 直接使用java.exe命令来运行某个主类

类加载器:

负责将class文件加载到内存中,并为之生成对应的Class对象

类加载器的组成:

Bootstrap ClassLoader:根类加载器

也被称为引导类加载器,负责java核心类的加载:比如System,String等,在JDK中JRE的lib目录下rt.jar文件

Extension ClassLoader:扩展类加载器

负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下ext目录

System ClassLoader:系统类加载器

负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

 

2. 反射

java反射机制是在运行状态中,对于任意一个类,都能够直到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为反射机制,简单来说就是通过Class文件,来使用该文件中的成员变量,构造方法,成员方法。

如果想要解剖一个类,必须先要获取该类的字节码文件对象,而解剖使用的就是class类中的方法,所以要先获取到每个字节码文件对应的Class类型的对象

使用步骤

A:获取Class文件对象(Class类对象)

1. 通过Object类的getClass()方法

2. 通过数据类型的静态属性class

3. Class s = Class.forName("类名"),类名需要是完整路径,包名.类名

B:Class类:

  成员变量:Field

  构造方法:Constructor

    获取所有公共的构造方法:public Constructor[] getConstructors();

    获取所有的构造方法:public Constructor[] getDeclaredConstructors();

    获取指定的构造方法:public Constructor<T> getConstructor(Class<?>... parameterTypes)

  成员方法:Method

    public Method getMethod(String name,Class<?>... parameterTypes)
    第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型

    public Object invoke(Object obj,Object... args)
    返回值是Object接收,第一个参数表示对象是谁,第二参数表示调用该方法的实际参数

public class Reflect {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
        Person p = new Person();
        Person e = new Person();
        
        Class c = p.getClass();
        Class l = e.getClass();            // Object的getClass()获取
        
        Class<Person> a = Person.class;                // class静态属性
        
        Class s = Class.forName("day_07.Person");
        
        System.out.println(p == e);        // false
        System.out.println(c == l);        // true
        System.out.println(c == a);        // true
        System.out.println(c == s);        // true
        
        Constructor[] cons = s.getConstructors();    // 返回所有的公共构造方法
//        Constructor[] cons = s.getDeclaredConstructors();    // 所有的构造方法
        for(Constructor con : cons){
            System.out.println(con);
        }
        /* public day_07.Person()
         * public day_07.Person(java.lang.String,int)
         */
        
        //使用无参构造
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();
        System.out.println(obj);
        /* public T newInstance(Object... initargs)
         * 使用该Constructor对象表示的构造方法来创建该构造方法的申明类的新实例,并用指定的参数初始化该实例
         */
        
        //使用带参构造
        con = c.getConstructor(String.class, int.class);
        Object obe = con.newInstance("xiaojignzi", 20);
        System.out.println(obe);
        
        // 获取私有构造方法
        con = c.getDeclaredConstructor(String.class);
        con.setAccessible(true);     // 设置私有可以访问
        obe = con.newInstance("小镜子");
        System.out.println(obe);
        
        // 获取成员变量并使用
        Field[] fields = c.getDeclaredFields();
        for(Field field : fields){
            System.out.println(field);
        }
        
        // 获取单个(私有)成员变量,获取name并对其赋值
        con = c.getConstructor();
        obj = con.newInstance();
        Field nameField = c.getDeclaredField("name");    // name是私有的修饰,所以需要加declared
        nameField.setAccessible(true);
        nameField.set(obj, "北京");    // 给obj对象的AddressField的字段设置为北京
        System.out.println(obj);
        
        // 获取所有方法
//        Method[] methods = c.getMethods();    // 获取子类和父类的所有方法
        Method[] methods = c.getDeclaredMethods();    // 获取子类的所有方法
        for(Method method : methods){
            System.out.println(method);
        }
        
        // 获取单个方法并使用
        Method mt = c.getMethod("toString");
        String st = (String) mt.invoke(obj);
        System.out.println(st);
        mt = c.getMethod("setName", String.class);
        mt.invoke(obj, "wangwang");
        System.out.println(obj);
        
        // 获取私有单个方法
        mt = c.getDeclaredMethod("getMessage");
        mt.setAccessible(true);
        mt.invoke(obj);
    }

}

 通过反射运行配置文件:

class.txt:
className = day_07.Person
methodName = getName

public class Runfile {
    public static void main(String[] args) throws Exception{
        //加载键值对数据
        Properties prop = new Properties();
        FileReader fr = new FileReader("class.txt");
        prop.load(fr);
        fr.close();
        
        // 获取数据
        String className = prop.getProperty("className");
        String methodName = prop.getProperty("methodName");
        
        Class c = Class.forName(className);
        Constructor con = c.getConstructor();
        Object obj =con.newInstance();
        Method m = c.getMethod(methodName);
        System.out.println(m.invoke(obj));
    }
}

通过反射写一个通用的设置某个对象的某个属性为指定值

public class Tool {
    public void setProperty(Object obj, String propertyName, Object value) throws Exception, SecurityException{
        // 根据对象获取字节码文件对象
        Class c = obj.getClass();
        // 获取该对象的propertyName成员变量
        Field field = c.getDeclaredField(propertyName);
        // 取消访问检查
        field.setAccessible(true);
        // 给对象的成员变量赋值为指定值
        field.set(obj, value);
    }
}

public class UseTool {
    public static void main(String[] args) throws Exception{
        Person p = new Person();
        Tool t = new Tool();
        t.setProperty(p, "name", "xiaojignzi");
    }
}

 

3. 动态代理:

代理:本来自己应该做的事情,请了别人来做,被请的人就是代理对象

动态代理:在程序运行过程中产生的对象

程序的运行过程中产生对象其实就是反射的内容,动态代理就是通过反射来生成一个代理

在java中,java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成一个动态代理对象,JDK提供的代理只能针对接口做代理,更强大的代理是cglib

Proxy类中的方法创建动态代理类对象:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)最终会调用InvocationHandler的方法

 InvocationHandler

Object invoke(Object proxy, Method method, Object[]):处理代理实例上的方法调用并返回结果 

获取系统的时间

public class GetTime {

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

}

 

4. 模板设计模式

模板方法模式就是定义一个算法的骨架,把具体的实现算法延迟到子类中实现

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

缺点:如果算法骨架有修改的话,需要修改抽象类

 

public class Timetest {
    public static void main(String[] args){
        GetTime gt = new UseGetTime();
        System.out.println(gt.getTime() + "毫秒");
    }
}

public class UseGetTime extends GetTime {

    @Override
    public void code() {
        // TODO Auto-generated method stub
        BufferedOutputStream bos = null;
        BufferedInputStream bis = null;
        try{
            bos = new BufferedOutputStream(new FileOutputStream("copy.jpg"));
            bis = new BufferedInputStream(new FileInputStream("abc.jpg"));
            byte[] bys = new byte[1024];
            int len = 0;
            while((len = bis.read(bys)) != -1){
                    bos.write(bys, 0, len);
            }
            bis.close();
            bos.close();
        }catch(IOException e){
            e.printStackTrace();
        }
    }

}

public abstract class GetTime {

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

}

 

5. 装饰模式

装饰模式就是使用被装饰类的一个子类的实例,在客户端将这个子类的实例交给装饰类,是继承的替代方案

优点:通过装饰模式,可以提供比继承更加灵活的扩展对象的功能,它可以动态的添加对象的功能,并且可以随意的组合这些功能

缺点:正是因为可以随意组合,所以可能出现一些不合理的逻辑

装饰模式可以任意组合

public interface Phone {
    public abstract void call();
}

public class PhoneDecorate implements Phone{
    private Phone p;
    
    public PhoneDecorate(Phone p) {
        super();
        this.p = p;
    }

    @Override
    public void call() {
        this.p.call();
    }

}

public class IPhone implements Phone {

    @Override
    public void call() {
        // TODO Auto-generated method stub
        System.out.println("手机可以打电话");
    }

}

public class MusicPhoneDecorate extends PhoneDecorate{

    public MusicPhoneDecorate(Phone p) {
        super(p);
        // TODO Auto-generated constructor stub
    }
    
    @Override 
    public void call(){
        super.call();
        System.out.println("手机可以听音乐");
    }
    
}

public class RingPhoneDecorate extends PhoneDecorate {

    public RingPhoneDecorate(Phone p) {
        super(p);
        // TODO Auto-generated constructor stub
    }
    
    @Override
    public void call(){
        System.out.println("手机可以听彩铃");
        super.call();
    }
}

public class PhoneUse {
    public static void main(String[] args) {
        Phone p = new IPhone();
        PhoneDecorate pd = new RingPhoneDecorate(p);
        pd.call();
        pd = new MusicPhoneDecorate(p);
        pd.call();
        System.out.println("------------");
        // 装饰模式可以任意组合
        pd = new RingPhoneDecorate(new MusicPhoneDecorate(p));
        pd.call();
        // 以下也是装饰
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        Scanner sc = new Scanner(System.in);
    }
}

 

6. 枚举

将变量的值一一列出来,变量的值只限于列举出来的值范围内。

注意事项:

定义枚举类要使用关键字enum

所有的枚举类都是Enum的子类

枚举类的第一行上必须是枚举项,最后一个枚举项的后面的分号是可以省略的,但是如果枚举类后面有其它东西,建议不要省略

枚举类可以有构造器,但是必须是private的,它默认也是private的,枚举类的用法比较特殊

枚举类可以在switch中使用

自己编写枚举类:

public abstract class Direction {
    String name;
    // 创建四个实例
    public static final Direction FRONT = new Direction(""){

        @Override
        public void show() {
            // TODO Auto-generated method stub
            System.out.println("");
        }
        
    };
    public static final Direction BEHIND = new Direction(""){

        @Override
        public void show() {
            // TODO Auto-generated method stub
            System.out.println("");
        }
        
    };
    public static final Direction LEFT = new Direction(""){

        @Override
        public void show() {
            // TODO Auto-generated method stub
            System.out.println("");
        }
        
    };
    public static final Direction RIGHT = new Direction(""){

        @Override
        public void show() {
            // TODO Auto-generated method stub
            System.out.println("");
        }
        
    };
    
    // 构造私有,外界不能创建对象
    private Direction(String name){
        this.name = name;
    }
    
    public void getName(){
        System.out.println(name);
    }
    
    public abstract void show();
}

public class DirectionUse {
    public static void main(String[] args){
        Direction d = Direction.FRONT;
        System.out.println(d);
        d.show();
    }
}

最简单的枚举实例:

public enum Directions {
    FRONT, BEHIND, LEFT, RIGHT;
}

public class DirectionsUse {
    public static void main(String[] args){
        Directions d = Directions.FRONT;
        System.out.println(d);
        
        
    }
}

特殊用法

public enum Directions {
    FRONT("前"), BEHIND("后"), LEFT("左"), RIGHT("右");
    
    private String name;
    private Directions(String name){
        this.name = name;
    }
    
    public String getName(){
        return name;
    }
}

public class DirectionsUse {
    public static void main(String[] args){
        Directions d = Directions.FRONT;
        System.out.println(d);
        System.out.println(d.getName());
        
    }
}

带抽象方法的枚举类

public enum Directions {
    FRONT("前"){

        @Override
        public void show() {
            // TODO Auto-generated method stub
            System.out.println("前");
        }
        
    }, BEHIND("后"){

        @Override
        public void show() {
            // TODO Auto-generated method stub
            System.out.println("后");
        }
        
    }, LEFT("左"){

        @Override
        public void show() {
            // TODO Auto-generated method stub
            System.out.println("左");
        }
        
    }, RIGHT("右"){

        @Override
        public void show() {
            // TODO Auto-generated method stub
            System.out.println("右");
        }
        
    };
    
    private String name;
    private Directions(String name){
        this.name = name;
    }
    
    public String getName(){
        return name;
    }
    
    public abstract void show();
}

public class DirectionsUse {
    public static void main(String[] args){
        Directions d = Directions.FRONT;
        System.out.println(d);
        System.out.println(d.getName());
        d.show();
    }
}

 可以使用switch

switch(d){
        case FRONT:
            System.out.println("你选择了前");
            break;
        case BEHIND:
            System.out.println("你选择了后");
            break;
        case LEFT:
            System.out.println("你选择了左");
            break;
        case RIGHT:
            System.out.println("你选择了右");
            break;
        }

枚举类的方法

public class EnumMethod {
    public static void main(String[] args){
        Directions d1 = Directions.FRONT;
        Directions d2 = Directions.BEHIND;
        Directions d3 = Directions.LEFT;
        Directions d4 = Directions.RIGHT;
        
        System.out.println(d1.compareTo(d2));    // -1
        System.out.println(d1.compareTo(d3));    // -2
        System.out.println(d1.compareTo(d4));    // -3
        
        // String name();
        System.out.println(d1.name());
        System.out.println(d2.name());
        
        // int ordinal()
        System.out.println(d1.ordinal());    // 0
        System.out.println(d2.ordinal());    // 1
        System.out.println(d3.ordinal());    // 2
        System.out.println(d4.ordinal());    // 3
        
        // String toString()
        System.out.println(d1.toString());    // FRONT    可以重写
        
        // <T> T valueOf(Class<T> type, String name) 创建一个name枚举类型的对象
        Directions d5 = Enum.valueOf(Directions.class, "FRONT");
        System.out.println(d5.getName());
        
        // values() 此方法在文档中查询不到,但是每一个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便
        Directions[] dirs = Directions.values();
        for(Directions dir : dirs){
            System.out.println(dir);    // FRONT BEHIND LEFT RIGHT
        }
    }
}

 

7. JDK7新特性

格式:try-with-resources语句

try(必须是java.lang.AutoCloseable的子类对象){}

好处:资源自动释放,不需要close()了

把该需要关闭的资源定义在这里

主要流体系的对象是这个接口的子类

public class TryNew {
    public static void main(String[] args){
        try(FileReader fr = new FileReader("class.txt");
                FileWriter fw = new FileWriter("cop.txt")){
            int ch = 0;
            while((ch = fr.read()) != -1){
                fw.write(ch);
            }
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

 

posted @ 2018-09-10 15:57  风影旋新月  阅读(294)  评论(0编辑  收藏  举报