《JAVA从入门到精通视频》笔记:反射、资源绑定器、类加载器、可变长度参数

1.反射机制有什么用?
       通过Java语言中的反射机制可以操作字节码文件(读和修改字节码文件),类似于黑客。通过反射机制可以操作代码片段。

2.反射机制的相关类在哪个包下?java.lang.reflect.*

3.反射机制相关的重要的类有哪些?
       java.lang.Class:代表字节码文件;
       java.lang.reflect.Method:代表字节码中的方法字节码;
       java.lang.reflect.Constructor:代表字节码中的构造方法字节码;
       java.lang.reflect.Field:代表字节码中的属性字节码。

4.操作一个类的字节码,首先需要获取到这个类的字节码,怎么获取java.lang.Class实例呢?
(1)第一种方式:Class c = Class.forName(“完整类名带包名”);

/*
Class.forName()
	1.静态方法
	2.方法的参数是一个字符串
	3.字符串需要的是一个完整类名
	4.完整类名必须带有包名
*/
public class ReflectTest {
    public static void main(String[] args) {
        try {
            Class c1 = Class.forName("java.lang.String");  //c1代表String.class文件,或者说c1代表String类型
            Class c2 = Class.forName("java.util.Date");  //c2代表Date.class文件
            Class c3 = Class.forName("java.lang.Integer");  //c3代表Integer.class文件 
            Class c4 = Class.forName("java.lang.System");  //c4代表System.class文件
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

(2)第二种方式:Class c = 对象.getClass();
       java中任何一个对象都有一个方法:getClass()。

		String s = "abc";
        Class x = s.getClass();
        //System.out.println(c1==x); 结果为true

        Date t = new Date();
        Class y = t.getClass();
        //System.out.println(c2==y); 结果为true

(3)java语言中任何一种类型,包括基本数据类型,都有.class属性。

		Class z = String.class;
		//System.out.println(x==z); 结果为true
        Class k = Date.class;
        //System.out.println(y==k); 结果为true
        Class f = int.class;
        Class e = double.class;

5.获取到Class之后,能干什么?
       通过反射机制,获取Class,通过Class来实例化对象。

package com.lilanlan.java.reflect;

public class ReflectTest01 {
    public static void main(String[] args) {
        Class c = null;
        try {
            c = Class.forName("com.lilanlan.java.bean.User");
            Object obj = c.newInstance(); //newInstance()这个方法会调用User这个类的无参数构造方法,完成对象的创建。
            System.out.println(obj);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

package com.lilanlan.java.bean;

public class User {
    public User() {
        System.out.println("User的无参构造方法执行");
    }
}

注意:newInstance()方法内部实际上调用了无参构造方法,因此必须保证无参构造方法存在!

6.为什么要使用反射创建对象呢?反射更加灵活!

假设在模块目录下创建一个文件classinfo.properties,在文件中输入className=com.lilanlan.java.bean.User
package com.lilanlan.java.bean;

public class User {
    public User() {
        System.out.println("User的无参构造方法执行");
    }
}

package com.lilanlan.java.reflect;

import java.io.FileReader;
import java.util.Properties;

public class ReflectTest02 {
    public static void main(String[] args) throws Exception{
        FileReader reader = new FileReader("ch01test/classinfo.properties");
        Properties pro = new Properties();
        pro.load(reader);
        reader.close();
        String className = pro.getProperty("className");

        Class c = Class.forName(className);
        Object o = c.newInstance();
        System.out.println(o);
    }
}

       这样来看的话,通过反射机制创建对象,可以在不改变Java源代码的基础之上,做到不同对象的实例化。 符合OCP原则:对扩展开放,对修改关闭。

7.执行Class.forName()会发生什么?

package com.lilanlan.java.reflect;

public class ReflectTest03 {
    public static void main(String[] args) {
        try {
            Class.forName("com.lilanlan.java.reflect.MyClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class MyClass{
    static {
        System.out.println("静态代码块执行!");
    }
}
//运行ReflectTest03,结果输出:静态代码块执行!

       这说明,Class.forName()这个方法的执行会导致:类加载。 如果只是希望一个类的静态代码块执行,其他代码一律不执行,可以使用Class.forName(“完整类名带包名”);

8.一种比较通用的路径使用方式
       这种路径即使在代码更换位置时也通用,但是该方法的使用前提是文件必须在类路径下(src是类的根路径)。

String path1 = Thread.currentThread().getContextClassLoader().getResource("classinfo2.properties").getPath();
System.out.println(path1);

String path2 = Thread.currentThread().getContextClassLoader().getResource("com/lilanlan/java/bean/db.properties").getPath();
System.out.println(path2);

/*
【解释】
	Thread.currentThread():当前线程对象。
	getContextClassLoader():线程对象的方法,可以获取到当前线程的类加载器对象。
	getResource():类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源。
*/

//结果输出:/D:/software/JetBrains/IdeaProjects/proxy-test/out/production/ch01test/classinfo2.properties

       采用以上代码可以拿到一个文件的绝对路径。

9.以流的形式直接返回

/*
String path = Thread.currentThread().getContextClassLoader().getResource("classinfo2.properties").getPath();
FileReader reader = new FileReader(path);
*/

//直接以流的形式返回
InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("classinfo2.properties");
Properties pro = new Properties();
pro.load(reader);
reader.close();
//通过key获取value
String className = pro.getProperty("className");
System.out.println(className);

10.资源绑定器
       java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。使用这种方式的时候,属性配置文件xxx.properties必须放在类文件下。

ResourceBundle bundle = ResourceBundle.getBundle("classinfo");
String className = bundle.getString("className");
System.out.println(className);

       属性配置文件扩展名必须是.properties,并且在写路径的时候,路径后面的扩展名不能写。

11.关于JDK中自带的类加载器
       JDK中自带了三个类加载器:启动类加载器、扩展类加载器、应用(系统)类加载器。

       假设有这样一段代码:String s = “abc”;
       代码在开始执行之前,会将所需要类全部加载到JVM当中。通过类加载器加载,以上代码类加载器会找到String.class文件,找到就加载,那么怎样进行加载呢?

  • 首先通过“启动类加载器”加载。注意:启动类加载器专门加载java\jdk1.8.0_101\jre\lib\rt.jar
    (rt.jar中都是JDK最核心的类库)
  • 如果通过“启动类加载器”加载不到的时候,会通过“扩展类加载器”加载。注意:扩展类加载器专门加载java\jdk1.8.0_101\jre\lib\ext\*.jar
  • 如果“扩展类加载器”没有加载到,那么会通过“应用类加载器”加载。注意:应用类加载器专门加载classpath中的类。

双亲委托机制: java中为了保证类加载的安全,使用了双亲委托加载机制。优先从启动类加载器中加载(父),启动类加载器无法加载到,再从扩展类加载器(母)中加载。如果两者都加载不到,才会考虑从应用类加载器中加载。若这三者都无法加载到,则抛出ClassNotFoundException。

12.Field
(1)获取Field

public class Student {
    public int no;
    private String name;
    protected int age;
    boolean sex;
}

获取Student类中的属性:

public class ReflectTest02 {
    public static void main(String[] args) throws Exception {
        Class studentClass = Class.forName("com.bean.student");

        //获取类中所有public修饰的Field
        Field[] fs1 = studentClass.getFields();
        System.out.println(fs1.length);
        System.out.println(fs1[0].getName());

        System.out.println("-----------------------");

        //获取类中所有的Field
        Field[] fs2 = studentClass.getDeclaredFields();
        System.out.println(fs2.length);
        for(Field f:fs2){
            System.out.println(f.getName());
        }
    }
}

结果输出:

1
no
-----------------------
4
no
name
age
sex

(2)反编译Field

public class ReflectClass03 {
    public static void main(String[] args) throws Exception{

        StringBuilder s = new StringBuilder();

        Class studentClass = Class.forName("java.lang.Integer");
        s.append(Modifier.toString(studentClass.getModifiers())+" class "+studentClass.getSimpleName()+"{\n");

        Field[] fields = studentClass.getDeclaredFields();
        for(Field f:fields){
            s.append("\t");
            s.append(Modifier.toString(f.getModifiers()));
            s.append(" ");
            s.append(f.getType().getSimpleName());
            s.append(" ");
            s.append(f.getName());
            s.append(";\n");
        }
        s.append("}");
        System.out.println(s.toString());
    }
}

结果能输出:
在这里插入图片描述
(3)通过反射机制访问对象属性

public class ReflectTest04 {
    public static void main(String[] args) throws Exception{
        //以前访问对象
        Student student = new Student();
        student.no = 1111;
        System.out.println(student.no);

        System.out.println("------------------------");

        //通过反射机制访问对象
        Class studentClass = Class.forName("com.bean.Student");
        Object obj = studentClass.newInstance();
        Field nofield = studentClass.getDeclaredField("no");
        nofield.set(obj,3187);
        System.out.println(nofield.get(obj));
    }
}

(4)通过反射机制访问私有属性

//通过反射机制访问私有属性
		Class studentClass = Class.forName("com.bean.Student");
        Object obj = studentClass.newInstance();
        Field namefield = studentClass.getDeclaredField("name");
        namefield.setAccessible(true);
        namefield.set(obj,"Jackson");
        System.out.println(namefield.get(obj));

13.可变长度参数
语法:类型…

public class ArgsTest {
    public static void main(String[] args) {
        m1(1);
        m1(10,"lll");
        m1(10,"111","222");
        m1(10,"111","222","333");

        m2("zs","ls","ww");
        m2(new String[]{"我","是","中","国","人"});
    }

    public static void m1(int arg1,String... arg2){
        System.out.println("m方法执行了!");
    }

    public static void m2(String... arg){
        for(int i=0;i<arg.length;i++){
            System.out.println(arg[i]);
        }
    }
}

注意:
       - 可变长度参数要求参数个数可以是0~n个;
       - 可变长度参数在参数列表中必须在最后,且可变长度参数在参数列表中只能有一个;
       - 可变长度参数可以被当做一个数组来看待,可变长度参数有length属性,可以进行循环遍历;
       - 一个数组可以作为可变长度参数的实参传递。

14.Method
(1)获取method

public class UserService {

    public boolean login(String name,String password){
        if("admin".equals(name) && "123".equals(password)){
            return true;
        }
        return false;
    }

    public void logout(){
        System.out.println("系统已经安全退出!");
    }
}

public class ReflectTest06 {
    public static void main(String[] args) throws Exception{
        Class userServiceClass = Class.forName("com.lilanlan.java.service.UserService");
        Method[] methods = userServiceClass.getDeclaredMethods();
        System.out.println(methods.length);

        for(Method m:methods){
            System.out.println(Modifier.toString(m.getModifiers()) + " " + m.getReturnType().getSimpleName() + " " + m.getName());
            Class[] parameterTypes = m.getParameterTypes();
            for(Class p:parameterTypes){
                System.out.println(p.getSimpleName());
            }
        }
    }
}

结果输出:
在这里插入图片描述

(2)使用反射机制调用对象方法

public class ReflectTest07 {
    public static void main(String[] args) throws Exception{
        //以前访问对象方法
        UserService userService = new UserService();
        boolean loginSuccess = userService.login("admin","123");
        System.out.println(loginSuccess?"登陆成功":"登陆失败");

        System.out.println("---------------------------");

        //使用反射机制访问对象方法
        Class userServiceClass = Class.forName("com.lilanlan.java.service.UserService");
        Object obj = userServiceClass.newInstance();
        //java中通过方法名和参数列表区分方法
        Method loginMethod = userServiceClass.getDeclaredMethod("login",String.class,String.class);
        //调用方法的四要素:对象、方法、实参列表、返回值
        Object retvalue = loginMethod.invoke(obj,"admin","123");
        System.out.println((boolean)retvalue?"登陆成功":"登陆失败");
    }
}

结果输出:
在这里插入图片描述
15.Constructor
(1)获取constructor

public class ReflectTest08 {

    public static void main(String[] args) throws Exception{
        Class vipClass = Class.forName("com.lilanlan.java.bean.Vip");

        Constructor[] constructors = vipClass.getDeclaredConstructors();
        System.out.println(constructors.length);
        for(Constructor c:constructors){
            System.out.print(Modifier.toString(c.getModifiers())+" ");
            System.out.print(vipClass.getSimpleName());
            System.out.print("(");

            //参数列表
            Class[] parameters = c.getParameterTypes();
            for(Class p:parameters){
                System.out.print(p.getSimpleName()+" ");
            }

            System.out.println(")\n");
        }
    }
}

(2)反射机制调用构造方法

public class ReflectTest09{
    public static void main(String[] args) throws Exception{

        //使用反射机制怎样创建对象呢?
        Class vipClass = Class.forName("com.lilanlan.java.bean.Vip");
        //调用无参数构造方法
        Object obj = vipClass.newInstance();
        System.out.println(obj);

        System.out.println("----------------------------");

        //调用有参数构造方法
        //第一步:先获取到这个有参数的构造方法
        Constructor con = vipClass.getDeclaredConstructor(int.class,String.class,String.class,boolean.class);
        //第二步:调用构造方法new对象
        Object newObj = con.newInstance(110,"Jackson","1990-10-11",true);
        System.out.println(newObj);
    }
}

结果输出:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值