反射基础篇
定义:根据现有对象倒推类的组成
最核心:在JVM中任何一个类都有一个唯一的Class对象 ,此对象记录该类的组成结构,通过该class对象,可以反向查找到这个类的信息,称之为反射;
当类加载时由JVM产生,用户只能取得此对象,无法创建此对象;
1.获取一个类的对应的Class对象 (Class的C是大写)
要想在Java中应用反射,首先取得该类的class对象;有三种方法可以获取:
(1)调用Object提供的getClass方法
class Person {
}
public class Test1 {
public static void main(String[] args) {
Person per = new Person();
System.out.println(per.getClass());
}
}
(2)类名称 . class
class Person {
}
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException{
Class cla = Test1.class;
System.out.println(cla);
}
}
(3)调用Class类提供的静态方法:Class . forName(类的全名称)
class Person {
}
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException{
//可能产生ClassNotFoundException异常,需要用throws抛出,否则编译不通过
Class<?> cls = Class.forName("www.bitten.java.Person"); //需用“”括起来,因为参数是String类型
System.out.println(cls.getName());
}
}
2.当我们拿到一个类的class对象后,可以做什么呢?
1. 创建该类的新对象
反射与工厂模式
package www.bitten.java;
/**
* @Author : YangY
* @Description : 反射与工厂模式
* @Time : Created in 9:37 2019/3/17
*/
interface Buycomputer {
void buy();
}
class Lenove implements Buycomputer {
@Override
public void buy() {
System.out.println("买一台联想电脑");
}
}
class Mac implements Buycomputer {
@Override
public void buy() {
System.out.println("买一台苹果电脑");
}
}
class ComputerFactory {
public static Buycomputer getInstance(String computerClass) {
try {
Class<?> cls = Class.forName(computerClass);
Buycomputer computer = (Buycomputer) cls.newInstance();
return computer;
}catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (IllegalAccessException e){
e.printStackTrace();
}catch (InstantiationException e) {
e.printStackTrace();
}
return null;
}
}
public class Test1 {
public static void main(String[] args) {
ComputerFactory.getInstance("www.bitten.java.Lenove").buy();
}
}
2. 取得包名、父类、父接口信息
interface ILife {
}
interface IAnimal {
}
class Person implements ILife,IAnimal {
}
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException{
Class<?> cla = Person.class;
//打印包名
System.out.println(cla.getPackage().getName());
//打印它的父类名称
System.out.println(cla.getSuperclass().getName());
//用这个类的Class对象生成它的父接口数组
Class<?>[] clasArr = cla.getInterfaces(); //也可写成Class[] ....
//输出父接口
for(Class<?> element: clasArr) {
System.out.println(element);
}
}
}
输出:
www.bitten.java
java.lang.Object
interface www.bitten.java.ILife
interface www.bitten.java.IAnimal
3. 取得构造方法、普通方法、普通属性(⭐)
Constructor :用来描述一个类的构造方法的类
(1)取得所有参数构造方法:
- Class 类提供的getConstructors() :只能取得权限为public的构造方法 ;
- Class类提供的getDeclaredConstructors():取得所有权限的构造方法 ;
class Person {
private String name;
private int age;
public Person() {
}
Person(String name) {
}
private Person(String name, int age) {
}
}
public class Test1 {
public static void main(String[] args) throws NoSuchMethodException {
Class<?> cla = Person.class;
//只能取得权限为public的构造方法
Constructor<?>[] constructor = cla.getConstructors();
//取得所有权限的构造方法
Constructor<?>[] constructors = cla.getDeclaredConstructors();
//输出权限为public的所有构造方法
for (Constructor<?> constructor1 : constructor) {
System.out.println(constructor1);
}
//输出所有权限的构造方法
System.out.println("--------------------------");
for (Constructor<?> constructor2 : constructors) {
System.out.println(constructor2);
}
}
}
输出:
public www.bitten.java.Person()
public www.bitten.java.Person()
www.bitten.java.Person(java.lang.String)
private www.bitten.java.Person(java.lang.String,int)
(2)取得指定参数的构造方法
- Class类提供的getConstructor(参数) : 只能取得权限为public的构造方法
- Class类提供的getDeclaredConstructor(参数) : 可以取得类中所有构造方法,与权限无关
例码:
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name) {
}
public Person(String name, int age) {
}
}
public class Test1 {
public static void main(String[] args)throws NoSuchMethodException{
Class<?> cla = Person.class;
//注意:getConstructor这个方法只能取得权限为public的构造方法
Constructor constructor = cla.getConstructor(String.class,int.class);
System.out.println(constructor.getName());
}
}
输出:
www.bitten.java.Person
(3)创建新对象
利用反射创建新对象有两种方法:
- 通过Class类的newInstance()方法 ,此方法只能调用无参构造来产生实例化对象;
- 通过Constructor的newInstance方法获取指定参数构造方法实例化的对象;
(1):通过Class类的newInstance()方法
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name) {
}
public Person(String name, int age) {
}
public void fun() {
System.out.println("hello");
}
}
public class Test1 {
public static void main(String[] args)throws InstantiationException,IllegalAccessException{
Class<?> cla = Person.class;
//Class类的newInstance方法只能获取无参构造实例化的对象
Person per =(Person) cla.newInstance();
System.out.println(per);
per.fun();
}
}
输出:
www.bitten.java.Person@154617c
hello
结论:我们在编写Java程序时要养成保留无参构造的习惯 (⭐)
(2)通过Constructor的newInstance方法获取指定参数构造方法实例化的对象
class Person {
private String name;
private int age;
public Person() {
}
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
}
public void fun() {
System.out.println("hello");
}
public String getName() {
return name;
}
}
public class Test1 {
public static void main(String[] args)throws NoSuchMethodException,InstantiationException
,IllegalAccessException,InvocationTargetException {
Class<?> cla = Person.class;
Constructor constructor = cla.getConstructor(String.class);
Person per =(Person) constructor.newInstance("yy");
System.out.println(per);
System.out.println(per.getName());
}
}
(4)取得普通方法和调用普通方法
Class类中提供了获取所有普通方法和获取指定参数普通方法的方法;
1.获取所有普通方法(返回值类型都为Method[ ])
-
(1):getMethods(),该方法返回对应的所有普通方法(不能返回private修饰的方法) ,它还返回父类,包括Object类的相关方法,也能返回所实现接口的普通方法;
-
(2):getDeclaredMethods(),该方法返回对应的所有普通方法,包括private修饰的普通方法,它不会返回父类的方法,但它能返回所实现接口中的方法;
最大区别:(1)方法能返回所继承父类的方法,而(2)不能;⭐
2.获取指定参数的普通方法
public Method getMethod(String name, Class<?>... parameterTypes)
class PP {
public void funfun() {
}
}
class Person extends PP{
private String name;
public Person() {
}
public String getName() {
return name;
}
public void fun() {
}
public void setName(String name) {
this.name = name;
}
}
public class Test1 {
public static void main(String[] args) throws Exception{
Class cla = Person.class;
Person per = (Person)cla.newInstance();
//通过class对象的getDeclaredMethods()方法获取所有普通方法;
Method[] arrMethod = cla.getDeclaredMethods();
for(Method method:arrMethod) {
System.out.println(method);
}
Method method1 = cla.getMethod("setName", String.class);
Method method2 = cla.getMethod("getName"); //该方法需要抛出NoSuchMethodException;
//要调用普通方法,必须得先创建它的实例化对象,per则是实例化
method1.invoke(per,"yy");
System.out.println(method2.invoke(per)); //输出yy
}
}
输出:
public void www.bitten.java.Person.fun()
public java.lang.String www.bitten.java.Person.getName()
public void www.bitten.java.Person.setName(java.lang.String)
yy
可见getDeclaredMethod方法没有返回父类的方法;
(5)取得属性
前提:类中的所有属性一定在类对象实例化之后才会进行空间分配,所以此时如果要想调用类的属性,必须保证有实例化对象。通过反射的newInstance()可以直接取得实例化对象(Object类型)
在Class类中提供有两组取得属性的方法:
- 第一组(父类中)-取得类中全部属性: public Field[] getFields() throws SecurityException
- 第一组(父类中)-取得类中指定名称属性: public Field getField(String name) throws
NoSuchFieldException, SecurityException - 第二组(本类中)-取得类中全部属性: public Field[] getDeclaredFields() throws SecurityException
- 第二组( 本类中)-取得类中指定名称属性 : public Method getDeclaredMethod(String name, Class<?
而后就需要关注属性的核心描述类:java.lang.reflect.Field,在这个类之中有两个重要方法:
- 设置属性内容 : public void set(Object obj, Object value) throws IllegalArgumentException,
IllegalAccessException- 取得属性内容 : public Object get(Object obj) throws IllegalArgumentException,
IllegalAccessException