zoukankan      html  css  js  c++  java
  • 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();
            }
        }
    }
  • 相关阅读:
    Let和Const的使用
    Spring框架学习10——JDBC Template 实现数据库操作
    python 学习
    delphi
    mysql 客户端连接报错Illegal mix of collations for operation
    tnsping 不通
    orm总结
    other
    resultset 查询时返回多个相同值
    vlan 知识学习
  • 原文地址:https://www.cnblogs.com/feng-ying/p/9620268.html
Copyright © 2011-2022 走看看