JavaSE 进阶 - 第25章 反射机制

1、反射机制

1.1、什么是反射机制?反射机制有什么用?
	通过java语言中的反射机制可以操作字节码文件。
	优点类似于黑客。(可以读和修改字节码文件。)
	通过反射机制可以操作代码片段。
	
	反射机制:可以操作字节码文件(class文件。)
	作用:可以让程序更加灵活。

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

1.3、反射机制相关的重要的类

	第一、java.lang.Class:代表整个字节码,代表一个类型,代表整个类。

		(.java源文件编译后生成的以“.class”结尾的文件,即为字节码文件)

	第二、java.lang.reflect.Field:代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)。

	第三、java.lang.reflect.Method:代表字节码中的方法字节码。代表类中的方法。

	第四、java.lang.reflect.Constructor:代表字节码中的构造方法字节码。代表类中的构造方法

	

	【例】:————————————————————————————————————————————————————————————————
		public class User{		//-----------java.lang.Class
			
			int no;			//------------- Field

			public User(){		//------------- Constructor
			
			}
			public User(int no){
				this.no = no;
			}

			public void setNo(int no){   //----------- Method
				this.no = no;
			}
			public int getNo(){
				return no;
			}
		}
		————————————————————————————————————————————————————————————————

  

2、通过反射机制,获取类的字节码————>Class

2.1 获取Class的三种方式
	要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java.lang.Class实例?
	三种方式
		第一种:Class c = Class.forName("完整类名带包名");
		第二种:Class c = 对象.getClass();
		第三种:Class c = 任何类型.class;

		这三种获得的c,都是一样的,代表的是该类的.class字节码文件,或者说代表的是该类的类型
import java.util.Date;

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

        // 第二种方式,java中任何一个对象都有一个方法:getClass()
        String s = "abc";
        Class x = s.getClass(); // x代表String.class字节码文件,x代表String类型。
        System.out.println(c1 == x); // true(==判断的是对象的内存地址。)

        Date time = new Date();
        Class y = time.getClass();
        System.out.println(c2 == y); // true (c2和y两个变量中保存的内存地址都是一样的,都指向方法区中的字节码文件。)

        // 第三种方式,java语言中任何一种类型,包括基本数据类型,它都有.class属性。
        Class z = String.class; // z代表String类型
        Class k = Date.class; // k代表Date类型
        Class f = int.class; // f代表int类型
        Class e = double.class; // e代表double类型

        System.out.println(x == z); // true

    }
}

在这里插入图片描述

2.2 通过反射机制创建对象
	获取到Class,能干什么?
		通过Class的newInstance()方法来实例化对象。(以前都是直接new创建对象:User user = new User();)
		注意:newInstance()方法内部实际上调用了该类型的无参数构造方法,必须保证无参构造存在才可以。
		
			//c代表的就是日期Date类型
			Class c = Class.forName("java.util.Date");

			//实例化一个Date日期类型的对象
			Object obj = c.newInstance();

	【bean/User.java】+【reflect/ReflectTest02.java】
package com.yuming.java.bean;

public class User {
    public User(){
        System.out.println("无参数构造方法!");
    }

    // 定义了有参数的构造方法,系统默认无参数构造方法就没了。因此要自己手写
    public User(String s){

    }
}
package com.yuming.java.reflect;

import com.yuming.java.bean.User;

public class ReflectTest02 {
    public static void main(String[] args) {

        // 这是不使用反射机制,创建对象
        User user = new User();
        System.out.println(user);

        // 下面这段代码是以反射机制的方式创建对象。
        try {
            // 通过反射机制,获取Class,通过Class来实例化对象
            Class c = Class.forName("com.yuming.java.bean.User"); // c代表User类型。

            // newInstance() 这个方法会调用User这个类的无参数构造方法,完成对象的创建。
            // 重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的!
            Object obj = c.newInstance();

            System.out.println(obj); // com.yuming.java.bean.User@10f87f48
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}
2.3 相比较以前直接new创建对象,反射机制创建对象更有灵活性:
	第一步:通过IO流读取属性配置文件(可以回顾【第23章 IO流————8、IO + Properties联合使用】)
	第二步:获取到要创建对象的对应类名
	第三步:再通过反射机制实例化对象

	java代码写一遍,在不改变java源代码的基础之上,可以做到不同对象的实例化(创建)。
	非常之灵活。(符合OCP开闭原则:对扩展开放,对修改关闭。)

	【chapter25/classinfo.properties】+【reflect/ReflectTest03.java】
className=java.util.Date
package com.yuming.java.reflect;

import com.yuming.java.bean.User;

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

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

        // 这种方式代码就写死了。只能创建一个User类型的对象
        //User user = new User();

        // 以下代码是灵活的,代码不需要改动,可以修改配置文件,配置文件修改之后,可以创建出不同的实例对象。
        // 通过IO流读取classinfo.properties文件:className=java.util.Date
        FileReader reader = new FileReader("chapter25/classinfo.properties");
        // 创建属性类对象Map
        Properties pro = new Properties(); // key value都是String
        // 加载
        pro.load(reader);
        // 关闭流
        reader.close();

        // 通过key获取value
        String className = pro.getProperty("className");  //获取类名:java.util.Date
        //System.out.println(className);

        // 通过反射机制实例化对象
        Class c = Class.forName(className);
        Object obj = c.newInstance();  //newInstance()调用的是无参构造,创建对象
        System.out.println(obj); // Wed Jul 22 13:27:56 CST 2020
    }
}
2.4 【重要】——只让静态代码块执行,可以使用Class.forName(“完整类名”);
	如果只让一个类的静态代码块执行,其它代码一律不执行,
	可以使用:Class.forName("完整类名");
	这个方法的执行会导致类加载,类加载时,静态代码块执行。
	
	【reflect/ReflectTest04.java + MyClass.java】
package com.yuming.java.reflect;

public class MyClass {

    // 静态代码块在类加载时执行,并且只执行一次。
    static {
        System.out.println("MyClass类的静态代码块执行了!");
    }
}
package com.yuming.java.reflect;
/*
研究一下:Class.forName()发生了什么?
    记住,重点:
        如果你只是希望一个类的静态代码块执行,其它代码一律不执行,
        你可以使用:
            Class.forName("完整类名");
        这个方法的执行会导致类加载,类加载时,静态代码块执行。

静态代码块 在【第11章 this和static】讲到过

提示:
    后面JDBC技术的时候我们还需要。
 */
public class ReflectTest04 {
    public static void main(String[] args) {
        try {
            // Class.forName()这个方法的执行会导致:类加载。
            Class.forName("com.yuming.java.reflect.MyClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}		
2.5 获取类路径下文件的绝对路径
	——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
	// 这种方式获取文件绝对路径是通用的。(前提:这个文件必须在类路径下)
	String path = Thread.currentThread().getContextClassLoader().getResource("package1/UserTest01.class").getPath();
	
	System.out.println(path); 
	// 绝对路径:
	//          /E:/IdeaProjects/javase/out/production/chapter25/package1/UserTest01.class
	——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
	
	什么是类路径下?
		凡是在src下的都是类路径下。这个package1/UserTest01.class就是在src下面
		src是类的根路径.

	Thread.currentThread() 当前线程对象
	getContextClassLoader() 是线程对象的方法,可以获取到当前线程的类加载器对象。
	getResource() 【获取资源】这是类加载器对象的方法,当前线程的类加载器 默认从类的根路径下加载资源。
	
	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	String path = Thread.currentThread().getContextClassLoader()
			.getResource("写相对路径,但是这个相对路径从src出发开始找").getPath();	

	String path = Thread.currentThread().getContextClassLoader()
			.getResource("abc").getPath();		
			
			//必须保证src下有abc文件。

	String path = Thread.currentThread().getContextClassLoader()
			.getResource("a/db").getPath();		
			
			//必须保证src下有a目录,a目录下有db文件。
	
	String path = Thread.currentThread().getContextClassLoader()	
			.getResource("com/cc/test.properties").getPath();	
			
			 //必须保证src下有com目录,com目录下有cc目录,cc目录下有test.properties文件。

	这种方式是为了获取一个文件的绝对路径。(通用方式,不会受到环境移植的影响。)
	但是该文件要求放在类路径下,换句话说:也就是放到src下面。
	src下是类的根路径。
	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2.6 路径以流的形式直接返回
	// 以流(InputStream字节输入流)的形式直接返回
	InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("com/cc/test.properties");
	
	【IoPropertiesTest.java】同【第23章 IO流】的【IoPropertiesTest01.java】
public class IoPropertiesTest {
    public static void main(String[] args) throws Exception{

        // 获取一个文件的绝对路径了!!!!!
        /*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);   // java.util.Date
    }
}
/*注:这里的classinfo2.properties是在chapter25/src/classinfo2.properties
      属性文件classinfo2.properties中写了:
className=java.util.Date
*/
2.7 【重要】——使用资源绑定器,快速读取属性配置文件
	java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。
	第一、资源绑定器,只能绑定xxx.properties文件。
	第二、并且这个文件必须在类路径下(即src下面)。
	第三、文件扩展名也必须是properties,
	第四、并且在写路径的时候,路径后面的扩展名不能写,只能写前面的路径和xxx

		ResourceBundle bundle = ResourceBundle.getBundle("com/cc/test");  //文件为test.properties
		String value = bundle.getString(key);  //通过key来获取value

	【ResourceBundleTest.java】
	
	这种方式 比【第23章 IO流】的【IoPropertiesTest01.java】要简便很多!
import java.util.ResourceBundle;

public class ResourceBundleTest {
    public static void main(String[] args) {

        ResourceBundle bundle = ResourceBundle.getBundle("package01/db");

        String username = bundle.getString("username");
        System.out.println(username); // zhangsan

    }
}

/*注:这里的db.properties是在chapter25/src/package01/db.properties
      属性文件db.properties中写了:
username=zhangsan
*/

  

3、通过反射机制,访问对象的属性————>Field

【实例1】通过反射机制,获取类当中所有的属性Field

package com.yuming.java.bean;

// 反射属性Field
public class Student {
    // Field翻译为字段,其实就是属性/成员
    // 4个Field,分别采用了不同的访问控制权限修饰符
    public int no;
    private String name;
    boolean sex;
    protected int age;
    public static final double MATH_PI = 3.1415926; //常量
}
package com.yuming.java.reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

/*
反射Student类当中所有的Field(了解一下)
 */
public class ReflectTest05 {
    public static void main(String[] args) throws Exception{  //异常先暂时放这里,正确做法应该try。catch。

        // 获取整个类
        Class studentClass = Class.forName("com.yuming.java.bean.Student");

        /*String className = studentClass.getName();
        System.out.println("完整类名:" + className);  // 完整类名:com.yuming.java.bean.Student

        String simpleName = studentClass.getSimpleName();
        System.out.println("简类名:" + simpleName); // 简类名:Student*/

        //  1、获取类中所有的public修饰的Field——.getFields()
        Field[] fields = studentClass.getFields();
        System.out.println(fields.length);               // 2  (数组中只有2个元素)
        // 取出第一个Field
        Field f = fields[0];
        // 取出这个Field它的名字
        String fieldName = f.getName();
        System.out.println(fieldName);                    // no

        //  2、 获取所有的Field——.getDeclaredFields()
        Field[] fs = studentClass.getDeclaredFields();
        System.out.println(fs.length);                    // 5

        System.out.println("==================================");
        // 遍历
        for(Field field : fs){
            // 1、获取属性的修饰符代号
            int i = field.getModifiers(); // 返回的修饰符是一个数字,每个数字是修饰符的代号!!!
            System.out.println(i);
            // 2、将这个“代号”数字转换成“字符串”
            String modifierString = Modifier.toString(i);
            System.out.println(modifierString);
            // 3、获取属性的类型
            Class fieldType = field.getType();
            //String fName = fieldType.getName();
            String fName = fieldType.getSimpleName();
            System.out.println(fName);
            // 4、获取属性的名字
            System.out.println(field.getName());
            System.out.println("~~~~~~~~~~~~~~~~~~");
        }
    }
}
/*java.lang.reflect.Modifier 里面有不同修饰符对应的“代号”:
默认(无)---0
public---1
private---2
protected---4
public static final---25(1+8+16)
 */

/*运行结果:
2
no
5
==================================
1
public
int
no
~~~~~~~~~~~~~~~~~~
2
private
String
name
~~~~~~~~~~~~~~~~~~
0

boolean
sex
~~~~~~~~~~~~~~~~~~
4
protected
int
age
~~~~~~~~~~~~~~~~~~
25
public static final
double
MATH_PI
~~~~~~~~~~~~~~~~~~
 */

不同修饰符对应的“代号”:
在这里插入图片描述

【实例2】反编译一个类的所有属性Field(了解一下)

package com.yuming.java.reflect;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

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

        // 创建这个是为了拼接字符串。
        StringBuilder s = new StringBuilder();

        Class studentClass = Class.forName("com.yuming.java.bean.Student");
        //Class studentClass = Class.forName("java.lang.Thread");

        //追加 类修饰符+class+类名+{
        s.append(Modifier.toString(studentClass.getModifiers()) + " class " + studentClass.getSimpleName() + " {\n");

        Field[] fields = studentClass.getDeclaredFields(); //  获取所有的属性Field
        for(Field field : fields){ //遍历所有的属性,追加到s上
            s.append("\t");     // 制表符
            s.append(Modifier.toString(field.getModifiers())); //修饰符
            s.append(" ");
            s.append(field.getType().getSimpleName());         //属性的类型
            s.append(" ");
            s.append(field.getName());                         //属性的名字
            s.append(";\n");   // ;换行符
        }

        //追加 }
        s.append("}");
        System.out.println(s);// 最后输出s
    }
}
/*结果:
public class Student {
	public int no;
	private String name;
	 boolean sex;
	protected int age;
	public static final double MATH_PI;
}
 */

【实例3】通过反射机制,访问一个java对象的属性 (重点)

package com.yuming.java.reflect;

import com.yuming.java.bean.Student;

import java.lang.reflect.Field;

/*
必须掌握:
    怎么通过反射机制访问一个java对象的属性?
        给属性赋值set
        获取属性的值get
 */
public class ReflectTest07 {
    public static void main(String[] args) throws Exception{

        // 一、我们不使用反射机制,怎么去访问一个对象的属性呢?
        Student s = new Student();
        // 1、给属性赋值
        s.no = 1111; //三要素:给s对象  的no属性   赋值1111
        // 2、读属性值
        System.out.println(s.no);// 两个要素:获取s对象 的no属性的值。


        // 二、使用反射机制,怎么去访问一个对象的属性。(set get)
        Class studentClass = Class.forName("com.yuming.java.bean.Student");
        Object obj = studentClass.newInstance(); // obj就是Student对象。(底层调用无参数构造方法)

        //  1、获取no属性(根据属性的名称来获取Field)
        Field noFiled = studentClass.getDeclaredField("no");

        //  2、给obj对象(Student对象)的no属性赋值
        /*
        虽然使用了反射机制,但是三要素还是缺一不可:
            要素1:obj对象。。要素2:no属性。。要素3:赋值2222值
        注意:反射机制让代码复杂了,但是为了一个“灵活”,这也是值得的。
         */
        noFiled.set(obj, 22222); // 给obj对象的no属性赋值2222

        //   3、读取属性的值
        System.out.println(noFiled.get(obj)); // 两个要素:获取obj对象的no属性的值。


        // 三、可以访问私有的属性吗?————可以
        //  1、获取name属性(根据属性的名称来获取Field)
        Field nameField = studentClass.getDeclaredField("name");
        //  2、打破封装(反射机制的缺点:打破封装,可能会给不法分子留下机会!!!)
        // 这样设置完之后,在外部也是可以访问private的。(setAccessible设置可以访问)
        nameField.setAccessible(true);
        //  3、给name属性赋值
        nameField.set(obj, "jackson");
        //  4、获取name属性的值
        System.out.println(nameField.get(obj));
    }
}
/*结果:
1111
22222
jackson
 */

  

4、【小插曲】————可变长度参数

int... args 这就是可变长度参数
语法是:类型...  (注意:一定是3个点。)

1、可变长度参数要求的参数个数是:0~N个。
2、可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有1个。
3、可变长度参数可以当做一个数组来看待
public class ArgsTest {
    public static void main(String[] args) {
        m();
        m(10);
        m(10, 20);
        // 编译报错,因为已经写了是int类型
        //m("abc");

        m2(100);
        m2(200, "abc");
        m2(200, "abc", "def");
        m2(200, "abc", "def", "xyz");

        m3("ab", "de", "kk", "ff");

        String[] strs = {"a","b","c"};
        // 也可以传1个数组
        m3(strs);

        // 直接传1个数组
        m3(new String[]{"我","是","中","国", "人"}); //没必要

        m3("我","是","中","国", "人");
    }

    public static void m(int... args){
        System.out.println("m方法执行了!");
    }

    // 必须在最后,只能有1个。
    public static void m2(int a, String... args1){

    }

    public static void m3(String... args){
        //args有length属性,说明args是一个数组!
        // 可以将可变长度参数当做一个数组来看。
        for(int i = 0; i < args.length; i++){
            System.out.println(args[i]);
        }
    }
}
/*
m方法执行了!
m方法执行了!
m方法执行了!
ab
de
kk
ff
a
b
c
我
是
中
国
人
我
是
中
国
人
 */

5、通过反射机制,调用对象的方法————>Method

【实例1】通过反射机制,获取类当中所有的方法Method

package com.yuming.java.service;

//用户业务类
public class UserService {
    /**
     * 登录方法
     * @param name 用户名
     * @param password 密码
     * @return true表示登录成功,false表示登录失败!
     */
    public boolean login(String name,String password){
        if("admin".equals(name) && "123".equals(password)){
            return true;
        }
        return false;
    }

    // 可能还有一个同名login方法
    // java中怎么区分一个方法,依靠方法名和参数列表。
    public void login(int i){
    }

    //退出系统的方法
    public void logout(){
        System.out.println("系统已经安全退出!");
    }
}
package com.yuming.java.reflect;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/*
作为了解内容(不需要掌握):
    反射Method
 */
public class ReflectTest08 {
    public static void main(String[] args) throws Exception{  //异常先暂时放这里,正确做法应该try。catch。

        // 获取类了
        Class userServiceClass = Class.forName("com.yuming.java.service.UserService");

        // 获取所有的Method(包括私有的!)
        Method[] methods = userServiceClass.getDeclaredMethods();
        System.out.println(methods.length); // 3

        // 遍历Method
        for(Method method : methods){
            // 1、获取修饰符列表
            System.out.println(Modifier.toString(method.getModifiers()));
            // 2、获取方法的返回值类型
            System.out.println(method.getReturnType().getSimpleName());
            // 3、获取方法名
            System.out.println(method.getName());
            // 4、获取方法的形参类型列表(一个方法的参数可能会有多个。)
            Class[] parameterTypes = method.getParameterTypes();
            for(Class parameterType : parameterTypes){
                System.out.println(parameterType.getSimpleName());
            }
            System.out.println("~~~~~~~~~~~~~~~~~~");
        }
    }
}
/*
3
public
void
logout
~~~~~~~~~~~~~~~~~~
public
boolean
login
String
String
~~~~~~~~~~~~~~~~~~
public
void
login
int
~~~~~~~~~~~~~~~~~~
 */

【实例2】反编译一个类的所有方法Method(了解一下)

package com.yuming.java.reflect;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

//了解一下,不需要掌握(反编译一个类的方法。)
public class ReflectTest09 {
    public static void main(String[] args) throws Exception{
        StringBuilder s = new StringBuilder();
        Class userServiceClass = Class.forName("com.yuming.java.service.UserService");
        //Class userServiceClass = Class.forName("java.lang.String");
        s.append(Modifier.toString(userServiceClass.getModifiers()) + " class "+userServiceClass.getSimpleName()+" {\n");

        Method[] methods = userServiceClass.getDeclaredMethods();
        for(Method method : methods){
            //public boolean login(String name,String password){}
            s.append("\t");
            s.append(Modifier.toString(method.getModifiers()));
            s.append(" ");
            s.append(method.getReturnType().getSimpleName());
            s.append(" ");
            s.append(method.getName());
            s.append("(");
            // 形式参数列表
            Class[] parameterTypes = method.getParameterTypes();
            for(Class parameterType : parameterTypes){
                s.append(parameterType.getSimpleName());
                s.append(",");
            }
            // 删除指定下标位置上的字符
            s.deleteCharAt(s.length() - 1); //删除最后面那个逗号 ,
            s.append("){}\n");
        }
        s.append("}");
        System.out.println(s);
    }
}
/*
public class UserService {
	public void logout){}
	public boolean login(String,String){}
	public void login(int){}
}
 */

【实例3】通过反射机制,调用一个java对象的方法(重点)

package com.yuming.java.reflect;

import com.yuming.java.service.UserService;

import java.lang.reflect.Method;

/*
重点:必须掌握,通过反射机制怎么调用一个对象的方法?
    五颗星*****

    反射机制,让代码很具有通用性,可变化的内容都是写到配置文件当中,
    将来修改配置文件之后,创建的对象不一样了,调用的方法也不同了,
    但是java代码不需要做任何改动。这就是反射机制的魅力。
 */
public class ReflectTest10 {
    public static void main(String[] args) throws Exception{
        // 一、 不使用反射机制,怎么调用方法
        //  1、创建对象
        UserService userService = new UserService();

        //  2、调用方法
        //要素1:对象userService。。要素2:login方法名。。要素3:实参列表。。要素4:返回值
        boolean loginSuccess = userService.login("admin","123");
        System.out.println(loginSuccess ? "登录成功" : "登录失败");  // 登录成功

        // 二、使用反射机制来调用一个对象的方法该怎么做?
        //  1、创建对象
        Class userServiceClass = Class.forName("com.yuming.java.service.UserService");
        Object obj = userServiceClass.newInstance();
        //  2、获取Method
        Method loginMethod = userServiceClass.getDeclaredMethod("login", String.class, String.class);//任何类型.class,可以获取该类的Class实例
        //Method loginMethod = userServiceClass.getDeclaredMethod("login", int.class);
        //  3、调用方法
        //要素1:obj对象。。要素2:loginMethod方法。。要素3:"admin","123" 实参。。要素4:retValue 返回值
        Object retValue = loginMethod.invoke(obj, "admin","123123");  //故意输个错误的密码
        System.out.println(retValue);   //false
    }
}
/*
登录成功
false
 */

6、通过反射机制,调用构造方法实例化对象————>Constructor

【实例1】反编译一个类的Constructor构造方法。

package com.yuming.java.bean;

public class Vip {
    int no;
    String name;
    String birth;
    boolean sex;

    public Vip() {  // 无参构造方法
    }

    public Vip(int no) { // 有1个参数的构造方法
        this.no = no;
    }

    public Vip(int no, String name) { // 有2个参数的构造方法
        this.no = no;
        this.name = name;
    }

    public Vip(int no, String name, String birth) { // 有3个参数的构造方法
        this.no = no;
        this.name = name;
        this.birth = birth;
    }

    public Vip(int no, String name, String birth, boolean sex) { // 有4个参数的构造方法
        this.no = no;
        this.name = name;
        this.birth = birth;
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Vip{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", birth='" + birth + '\'' +
                ", sex=" + sex +
                '}';
    }
}
package com.yuming.java.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;

//反编译一个类的Constructor构造方法。
public class ReflectTest11 {
    public static void main(String[] args) throws Exception{ //异常先暂时放这里,正确做法应该try。catch。
        StringBuilder s = new StringBuilder();
        Class vipClass = Class.forName("com.yuming.java.bean.Vip");
        //Class vipClass = Class.forName("java.lang.String");
        s.append(Modifier.toString(vipClass.getModifiers()));
        s.append(" class ");
        s.append(vipClass.getSimpleName());
        s.append("{\n");

        // 拼接构造方法
        Constructor[] constructors = vipClass.getDeclaredConstructors();
        for(Constructor constructor : constructors){
            //public Vip(int no, String name, String birth, boolean sex) {
            s.append("\t");
            s.append(Modifier.toString(constructor.getModifiers()));
            s.append(" ");
            s.append(vipClass.getSimpleName());
            s.append("(");
            //  拼接参数
            Class[] parameterTypes = constructor.getParameterTypes();
            for(Class parameterType : parameterTypes){
                s.append(parameterType.getSimpleName());
                s.append(",");
            }
            // 删除最后下标位置上的字符
            if(parameterTypes.length > 0){
                s.deleteCharAt(s.length() - 1);
            }
            s.append("){}\n");
        }
        s.append("}");
        System.out.println(s);
    }
}
/*
public class Vip{
	public Vip(int,String,String,boolean){}
	public Vip(int,String,String){}
	public Vip(int,String){}
	public Vip(int){}
	public Vip(){}
}
 */	

【实例2】通过反射机制,调用构造方法实例化java对象

package com.yuming.java.reflect;

import com.yuming.java.bean.Vip;

import java.lang.reflect.Constructor;

public class ReflectTest12 {
    public static void main(String[] args) throws Exception{
        // 一、不使用反射机制怎么创建对象
        Vip v1 = new Vip();
        Vip v2 = new Vip(110, "zhangsan", "2001-10-11", true);

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

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

        // 也可以这样,获取无参数构造方法
        Constructor con2 = c.getDeclaredConstructor();
        Object newObj2 = con2.newInstance();
        System.out.println(newObj2);
    }
}
/*
Vip{no=0, name='null', birth='null', sex=false}
Vip{no=110, name='jackson', birth='1990-10-11', sex=true}
Vip{no=0, name='null', birth='null', sex=false}
 */	

7、通过反射机制,获取父类以及父类型接口

package com.yuming.java.reflect;

//重点:给你一个类,怎么获取这个类的父类? 已经实现了哪些接口?
public class ReflectTest13 {
    public static void main(String[] args) throws Exception{

        // String举例
        Class stringClass = Class.forName("java.lang.String");

        // 获取String的父类
        Class superClass = stringClass.getSuperclass();
        System.out.println(superClass.getName());

        // 获取String类实现的所有接口(一个类可以实现多个接口。)
        Class[] interfaces = stringClass.getInterfaces();
        for(Class in : interfaces){
            System.out.println(in.getName());
        }
    }
}
/*
java.lang.Object
java.io.Serializable
java.lang.Comparable
java.lang.CharSequence
java.lang.constant.Constable
java.lang.constant.ConstantDesc
 */

【拓展】————关于JDK中自带的类加载器(了解)

1、什么是类加载器?
	专门负责加载类的命令/工具。
	ClassLoader

2、JDK中自带了3个类加载器
	启动类加载器:rt.jar
	扩展类加载器:ext/*.jar
	应用类加载器:classpath

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

		首先通过“启动类加载器”加载。
			注意:启动类加载器专门加载:C:\Program Files\Java\jdk1.8.0_101\jre\lib\rt.jar
			rt.jar中都是JDK最核心的类库。
		
		如果通过“启动类加载器”加载不到的时候,
		会通过"扩展类加载器"加载。
			注意:扩展类加载器专门加载:C:\Program Files\Java\jdk1.8.0_101\jre\lib\ext\*.jar


		如果“扩展类加载器”没有加载到,那么
		会通过“应用类加载器”加载。
			注意:应用类加载器专门加载:classpath中的类。
		
		classpath环境变量

4、java中为了保证类加载的安全,使用了双亲委派机制。
	启动类加载器:rt.jar ————》父加载器
	扩展类加载器:ext/*.jar————》母加载器
	应用类加载器:classpath

	双亲委派机制:
		优先从父加载器中加载,父加载器无法加载到,再从母加载器中加载,
		如果都加载不到,才会考虑从应用类加载器中加载。直到加载到为止。

传送门

上一章:JavaSE 进阶 - 第24章 多线程(二)
下一章:JavaSE 进阶 - 第26章 注解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
面向对象编程是一种编程范式,它将程序的构建和设计思路以面向对象的方式进行组织和实现。在Java中,面向对象编程是基于Java SE(Standard Edition)的一种编程方式。第07讲主要介绍了面向对象编程中的一些基本概念和关键术语。 在面向对象编程中,我们将程序中的数据和对数据的操作(方法)封装在一起,形成一个对象。对象由两部分构成:属性和方法。属性是用来描述对象的特征,而方法则是对象可以执行的操作。对象之间通过消息(方法调用)进行通信和交互。面向对象的核心思想是通过封装、继承和多态实现程序的复用和扩展。 封装是面向对象编程中的一个重要概念,它指的是将类的属性和方法进行封装,使得外部无法直接访问和修改对象的内部状态,只能通过公共的方法来操作属性和执行方法。封装提供了一种将数据和行为组合在一起的方式,可以保护数据的完整性和安全性。 继承是面向对象编程中的另一个重要概念,它指的是通过定义一个新的类来继承现有类的属性和方法。通过继承,子类可以继承父类的属性和方法,并可以在此基础上进行扩展和修改。继承提供了一种代码复用的机制,可以减少重复编码的工作量。 多态是面向对象编程的又一个重要概念,它指的是同一类型的对象在不同的情况下可以有不同的表现形式。多态通过方法的重写和方法的重载实现。方法的重写指的是在子类中重新定义和实现父类的方法,方法的重载指的是在同一个类中可以定义多个同名但参数列表不同的方法。 总结来说,面向对象编程是一种将程序组织和设计思路以对象为中心的编程方式。在JavaSE中,我们可以通过封装、继承和多态来实现面向对象编程的目标。封装可以提高程序的可维护性和可复用性,继承可以减少重复编码的工作量,多态可以灵活地操作对象。掌握这些基本概念和关键术语,可以帮助我们更好地理解和应用面向对象编程的思想。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值