Java - 基础 - 反射

Java - 基础 - 反射

反射机制

  1. 什么是反射

  2. 反射的作用

  3. 利用反射调用类结构

1.认识反射

正:正常情况下,先有类,之后才会有类的对象

反:可以利用对象找到对象的出处

​ 取得Classs对象:

public final Class<?> getClass()

该方法继承自Object类,所以,所有的类都有这个方法

public static void main(String[] args) {
    Date date = new Date();
    System.out.println(date.getClass());
}
//输出:
//class java.util.Date

2.Class类对象实例化

java.lang.Class是一个类。该类是反射的源头,该类有三种实例化方法,如下:

  • 调用Object类中的getClass()方法

    public static void main(String[] args) {
    	Date date = new Date();
    	Class <?> cls = date.getClass();
    	System.out.println(cls);
    }
    
  • 使用"类.class"取得

    public static void main(String[] args) {
    	Class <?> cls = Date.Class;
    	System.out.println(cls);
    }
    
  • 调用Class类提供的方法

    实例化Class对象:

    public static Class<?> forName(String className)
    throws ClassNotFoundException

    public static void main(String[] args) {
    	Class <?> cls = Class.forName("java.util.Date");
     System.out.println(cls);
    }
    

    此时可以不使用import进行导入

3.反射实例化对象

当拿到一个类的时候,使用new 实例化对象。如果存在Class类对象,就可以通过反射进行对象的实例化

实例化对象方法:

public T newInstance() throws InstantiationException, IllegalAccessException

示例

class Book{
	
	public Book() {
		System.out.println("********************");
	}
	public String toString() {
		return "这是一本书";
		
	}
}
public class test {
	public static void main(String[] args) throws Exception {
        //1.常规方法,new对象
		Book b = new Book();
		System.out.println(b);
		
        //2.反射对象
		Class<?> cls = Class.forName("com.String.Book");
		Object o = cls.newInstance();
        Book book = (Book)o;
		System.out.println(o);//输出b和o结果一样
	}
}

4.反射调用构造

当类中只有有参构造的时候,通过反射调用,会产生异常

错误代码示例

package com.String;

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

		Class<?> cls = Class.forName("com.t.Book");
		Object o = cls.newInstance();
		System.out.println(o);
	}
}

Book类,位于其他包

package com.t;

public class Book{
	private String title;
	private double price;
	
	public Book(String title , double price) {
		this.title = title ;
		this.price = price;
	}
	public String toString() {
		return "书名:"+this.title+",价格:"+this.price;
		
	}
}

异常信息

Exception in thread "main" java.lang.InstantiationException: com.t.Book
	at java.lang.Class.newInstance(Unknown Source)
	at com.String.test.main(test.java:11)
Caused by: java.lang.NoSuchMethodException: com.t.Book.<init>()
	at java.lang.Class.getConstructor0(Unknown Source)
	... 2 more

解决方法:

在Class类中,提供有获取构造方法的方法

  • 取得全部构造方法:

    public Constructor <?> [] getConstructors()

  • 取得一个指定参数的构造方法:

    public Constructor < T > getConstructor(Class<?>… parameterTypes)

上面两个方法的返回值**Constructor **是java.lang.reflect.Constructor类的对象,在这个类中提供有一个明确传递有参构造内容的实例化对象方法

public T newInstance(Object... initargs)

解决代码示例:

package com.String;

import java.lang.reflect.Constructor;

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

		Class<?> cls = Class.forName("com.t.Book");
		Constructor<?> con = cls.getConstructor(String.class,double.class);
		Object o = con.newInstance("java开发",79.8);
		System.out.println(o);
	}
}

简单JAVA类的开发之中,至少保留一个无参构造,反射调用不方便

5 .反射调用方法

类中的普通方法,只有在类产生实例化对象之后才可以调用。

实例化对象的方式有三种

  1. new一个对象
  2. 克隆一个对象
  3. 反射一个对象

在Class类中,提供有取得方法的操作

  • 取得一个类中的全部方法

    public Method[] getMethods()
    
  • 取得指定名称的方法

    public Method getMethod(String name, Class<?>... parameterTypes)
    

    name是方法名称

上面两个方法的返回值**Method **是java.lang.reflect.Method 类的对象

  • 调用方法:

    public Object invoke(Object obj,
                         Object... args)
    

示例代码

Book类,位于其他包

package com.t;

public class Book{
	private String title;

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}
}

调用方法:

package com.String;
import java.lang.reflect.Method;

public class test {
	public static void main(String[] args) throws Exception {
		String fielName = "title";
		Class<?> cls = Class.forName("com.t.Book");
		Object o = cls.newInstance();
		Method setMet = cls.getMethod("set" +initcap(fielName), String.class);
		Method getMet = cls.getMethod("get" +initcap(fielName));	
		setMet.invoke(o, "java开发");
		System.out.println(getMet.invoke(o));	
	}
	
	//首字母大写
	public static String initcap(String str) {
		return str.substring(0, 1).toUpperCase()+str.substring(1);
	}
}

6.反射调用成员

类中的属性,一定要在本类实例化对象产生之后才可以分配内存空间

  • 取得全部成员

    public Field[] getDeclaredFields()
    
  • 取得指定成员

    public Field getDeclaredField(String name)
    

上面两个方法的返回值Field是java.lang.reflect.Field类的对象,该类提供有如下常用方法

  • 取得属性内容

    public Object get(Object obj)
    
  • 设置属性内容

    public void set(Object obj,Object value)
    

使用示例:

Book类,位于其他包

package com.t;

public class Book{
	private String title;

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}
}

反射调用:

package com.String;

import java.lang.reflect.Field;

public class test {
	public static void main(String[] args) throws Exception {
		//1.实例化类对象
		Class<?> cls = Class.forName("com.t.Book");
		Object o = cls.newInstance();
		//取得成员
		Field titleField = cls.getDeclaredField("title");
		//取消封装,不取消无法访问private变量
		titleField.setAccessible(true);
		//设置成员变量
		titleField.set(o, "python");
		System.out.println(titleField.get(o));	
	}
}

构造方法和普通方法也可以取消封装

7.总结

  1. 实例化对象的方式多了一种:反射
  2. 简单JAVA类需要使用无参构造以及使用set\get方法的原因
上一篇:Python中创建单例模式的六种方式


下一篇:单例模式