JAVA Class25

学习内容:

1.反射

反射目的是在不修改代码的情况下,只需修改外部配置文件,实现调用不同类的不同方法。

(1)类的载入

当我们需要使用一个类时,我们要将这个类载入JVM,这里就要用到类载入的方法:

在我们实例化一个对象时,类会自动载入,另外,实例化一个子类对象会导致父类自动载入

public class Person {
    public String name;
    private int age;
    static {//注意这个静态代码块!
        System.out.println("静态代码块");
    }
    public Person() {
        super();
    }
    public Person(String name,int age) {
        super();
        this.name = name;
        this.age = age;
    }
} 
public class TestPerson {

    @SuppressWarnings({"rawtypes","unchecked"})//忽略多类型警告
    public static void main(String[] args) {
        try {
            Class c1 = Class.forName("edu.java.reflection.Person");//包名.类名
            Class c2 = new Person().getClass();
            //上述两种方式都会初始化静态属性,且只会执行一次!
            Class c3 = Person.class;//不会初始化静态属性
            //在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。
            //注: 准确的讲是一个ClassLoader下,一种类,只会有一个类对象存在。通常一个JVM下,只会有一个ClassLoader。
            System.out.println(c1==c2);//true
            System.out.println(c1.equals(c2));//true
            System.out.println(c1==c3);//true
            System.out.println(c1.equals(c3));//true
  }
}

(2)构造器的获取与利用构造器实例化对象:

public class TestPerson {

    @SuppressWarnings({"rawtypes","unchecked"})
    public static void main(String[] args) {
        try {
            Class c1 = Class.forName("edu.java.reflection.Person");//包名.类名
            //获取造器数组
            Constructor[] con = c1.getConstructors();//获取所有公共的构造器
            for(Constructor s:con) {
                System.out.println(s);
            }
            //获取空参构造器
            Constructor con = c1.getConstructor();
            //空参构造器实例化对象
            Object obj = con.newInstance();
            Person p = (Person)obj;//newInstance()返回的是Object,向下强转Person
            //有参构造器
            Class[] v = {String.class,int.class};
            Constructor con = c1.getConstructor(v);
            Object obj = con.newInstance("张三",20);
            Person p = (Person)obj;
            System.out.println(p);
            Constructor[] all = c1.getDeclaredConstructors();//获取所有构造器,包括私有的
            for(Constructor con:all) {
                System.out.println(con);
            }
            Constructor con = c1.getDeclaredConstructor(String.class);//获取私有的有参构造器
            con.setAccessible(true);//取消变量权限检查,可以访问私有化变量
            Object obj = con.newInstance("李四");
            Person p = (Person)obj;
            System.out.println(p);
            //空参构造快速生成对象
            Object obj = c1.newInstance();
            Person p = (Person)obj;
  }
}

(3)获取字段值

public class TestPerson {

    @SuppressWarnings({"rawtypes","unchecked"})
    public static void main(String[] args) {
        try {
            Class c1 = Class.forName("edu.java.reflection.Person");//包名.类名
            Field[] f = c1.getFields();//获取所有公共字段
            for(Field i:f) {
                System.out.println(i);
            }
            Constructor con = c1.getConstructor(String.class,int.class);
            Object obj = con.newInstance("张三",20);
            Person p = (Person)obj;
            Field f = p.getClass().getField("name");//获取对象的指定字段值
            f.set(p, "李四");
            System.out.println(p);
            Field age = p.getClass().getDeclaredField("age");//获取私有变量
            age.setAccessible(true);
            age.set(p, 30);
            System.out.println(p);
        } catch(Exception e) {
            e.printStackTrace();
        }
        
    }

}

(4)通过类获取方法

public class TestPerson {

    @SuppressWarnings({"rawtypes","unchecked"})
    public static void main(String[] args) {
        try {
            Class c1 = Class.forName("edu.java.reflection.Person");//包名.类名
            Constructor con = c1.getConstructor(String.class,int.class);
            Object obj = con.newInstance("张三",20);
            Person p = (Person)obj;
            Field f = p.getClass().getField("name");//获取对象的指定字段值
            f.set(p, "李四");
            System.out.println(p);
            Field age = p.getClass().getDeclaredField("age");//获取私有变量
            age.setAccessible(true);
            age.set(p, 30);
            System.out.println(p);*/
            //获取所有方法
            Method[] all = c1.getMethods();
            for(Method mm:all) {
                System.out.println(mm);
            }
            Method m1 = c1.getMethod("eat");
            m1.invoke(p);
            Method m2 = c1.getDeclaredMethod("sleep",String.class,int.class,double.class);//获取私有带参方法
            m2.setAccessible(true);
            m2.invoke(p,p.name,30,90);
        } catch(Exception e) {
            e.printStackTrace();
        }
        
    }

}

2.泛型擦除

JAVA的泛型是”假泛型“,不进class文件,所以利用反射,我们可以突破泛型限制添加数据

public class TestPerson {

    @SuppressWarnings({"rawtypes","unchecked"})
    public static void main(String[] args) {
        try {
            //泛型擦除
            ArrayList<String> list = new ArrayList<String>();
            list.add("abc");
            Class c = list.getClass();
            Method method = c.getMethod("add", Object.class);
            method.invoke(list, 1);
            System.out.println(list);//数字1被成功添加,JAVA的泛型是”假泛型“,不进class文件
            System.out.println(list.get(0));
            System.out.println(list.get(1));//但是获取会报类型转换失败错误
        } catch(Exception e) {
            e.printStackTrace();
        }
        
    }

}

3.泛型应用

public class Test {
     @SuppressWarnings({ "rawtypes", "unchecked" })//忽略多类型警告
    public static void main(String[] args) throws Exception {
        //从spring.txt中获取类名称和方法名称
        File sp = new File("e:/java/spring/spring.txt");
        Properties sc = new  Properties();
        sc.load(new FileInputStream(sp));
        String classname = (String)sc.get("class");
        //String methodname = (String)sc.get("method");
        String methodname = sc.getProperty("method");
        //根据类名称获取类对象
        Class cl = Class.forName(classname);
         //根据方法名称,获取方法对象
        Method m = cl.getMethod(methodname);
         //获取构造器
        Constructor c = cl.getConstructor();
        //根据构造器,实例化出对象
        Object service = c.newInstance();
        //调用对象的指定方法
        m.invoke(service);
        //如果要切换类及方法,只改变外部的txt文件内容即可,代码不用改
    }

}

 利用这一思想,我们可以对之前用过的DButil包做一下改动:

配置文件 DButil.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/Hero?characterEncoding=UTF-8
user=root
password=123456

做改动:

public class DButil {
    private DButil() {};
    public static Connection getConn() {
        try {
            Properties pro = new Properties();
            pro.load(new FileInputStream("properties/DButil.properties"));
            String driver = pro.getProperty("driver");
            String url = pro.getProperty("url");
            String user = pro.getProperty("user");
            String password = pro.getProperty("password");
            Class.forName(driver);
            Connection conn = DriverManager.getConnection(url, user, password);
            return conn;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("连接数据库失败!"+e);
        }
    }
    /**
     * 用来连接数据库
     * 返回Connection对象
     * */
    public static void close(Statement sta,Connection conn) {
        if(sta!=null) {
            try {
                sta.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if(conn!=null) {
            try {
                conn.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 用来关闭Statement Connection
     * 接收一个Statement对象、一个Connection对象
     * */
    public static void close(ResultSet rs,Statement sta,Connection conn) {
        if(rs!=null) {
            try {
                rs.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        close(sta,conn);
    }
    /**
     * 用来关闭ResultSet Statement Connection
     * 接收一个ResultSet对象、一个Statement对象、一个Connection对象
     * */
}
原文地址:https://www.cnblogs.com/whwjava/p/8966829.html