黑马程序员-【反射】

时间:2023-02-19 17:04:33

                           ------- android培训java培训、期待与您交流! ----------

反射的概念

   

    反射的引入:

    Object obj = new Student();

    若程序运行时接收到外部传入的一个对象,该对象的编译类型是Object,但程序又需要调用该对象运行类型的方法:

       1.若编译和运行类型都知道,使用 instanceof判断后,强转。

       2.编译时根本无法预知该对象属于什么类,程序只能依靠运行时信息来发现对象的真实信息,这时就必须使用反射了。

       3.要是想得到对象真正的类型,就得使用反射。

     简单的来说,反射机制指的是程序在运行时能够获取自身的信息。在java中,只要给定类的名字,那么就可以通过反射机制来获

类的所有信息。

 

反射的优缺点

    

   静态编译:在编译时确定类型,绑定对象,即通过。  

   动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。

   优点:是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发。

   缺点:影响系统性能

 

Class类

 

     java中的类是用来描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个类的实例

象来确定的,不同的实例对象有不同的属性值。java中所有的类也是一类事物,有其共性,都有所属的包,类名,属性的访问权

限,字段,方法等信息。我们对此进行抽象提取用于描述类的共性内容,这就出现了一个特有的类Class类。

 

    Class对象封装了一个java类中定义的成员变量、成员方法、构造方法、类名、包名等。

 

     一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以

它们在内存中的内容是不同的
  

    获得class类的方式:

         1、调用某个类的class属性获取Class对象,如Date.class会返回Date类对应的Class对象(其实就是得到一个类的一份字节码文件);

         2、调用某个对象的getClass()方法。该方法属于Object类;Class<?> clz = new Date().getClass();

         3、使用Class类的forName(String className)静态方法,className表示全限定名;如String的全限定名:java.lang.String;

 例子:

 

public class ClassDemo1 {
public static void main(String[] args) throws Exception {
//获得Class对象的方法(三种)
//一:调用属性
Class<String> c = String.class;
System.out.println(c);//打印结果:class java.lang.String String.class就表示JVM中一份表示String类的字节码
Class<String> c2 = String.class;
System.out.println(c == c2);//true都是String类的字节码 一个类在虚拟机中只有一份字节码;

//二:使用forName()方法
//Class cla = Class.forName("String");//ERROR,
Class<String> cla = (Class<String>)Class.forName("java.lang.String");//必须用上全限定名,否则报错
System.out.println(c == cla);//true

//三:利用对象调用Object的getClass方法;
Class c3 = new String().getClass();
System.out.println(c == c3);//ture
}
}


      预定义的Class对象:基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void通过class属性

也表示为 Class 对象;

  

利用class获取类的属性信息

    

class A {
}
interface B{
}
interface C{
}

public class BaseDemo3 extends A implements B,C{

//内部类
public class C{}
public interface D{}
public static void main(String[] args) {
//类可以,接口也可以
Class<BaseDemo3> c = BaseDemo3.class;
System.out.println(c);//class junereflect624.BaseDemo3

//得到包名
System.out.println(c.getPackage());//package junereflect624

//得到全限定名
System.out.println(c.getName());//junereflect624.BaseDemo3

//得到类的简称
System.out.println(c.getSimpleName());//BaseDemo3

//得到父类
/**
* Class<? super T> getSuperclass() 此处super表示下限
返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。
*/
System.out.println(c.getSuperclass().getSimpleName());//A,先获取父类,再获取父类的简称

//得到接口
System.out.println(c.getInterfaces());//[Ljava.lang.Class;@1b60280
Class[] arr = c.getInterfaces();
for (Class cla : arr) {
System.out.println(cla);//interface junereflect624.B interface junereflect624.C
}

//获得public修饰的类
/**
* Class<?>[] getClasses()
返回一个包含某些 Class 对象的数组,这些对象表示属于此 Class 对象所表示的类的成员的所有公共类和接口。 (如果内部类前面没有加上public的话那么得不到!)
*/
Class[] cl = c.getClasses();
System.out.println(cl.length);//在内部类没有加上public修饰的时候长度为0,加上就是2(获取的是公共的)
for (Class class1 : cl) {
System.out.println(class1);
}

//获得修饰符
int i = c.getModifiers();
System.out.println(i);//常量值1表示public
System.out.println(Modifier.toString(i));//直接打印出public
}
}

 


利用Class获得类构造方法Constructor

 

    Constructor类用于描述类中的构造方法

 

    Constructor<T> getConstructor(Class<?>... parameterTypes)

    返回该Class对象表示类的指定的public构造方法;

 

    Constructor<?>[] getConstructors()

    返回该Class对象表示类的所有public构造方法;

 

    Constructor<?>[] getDeclaredConstructors()

    返回该Class对象表示类的所有构造方法,和访问权限无关;

例子:

 

class Emp{
private String name;
private int age;
private Emp() {
}
Emp(String name){
}
public Emp(String name,int age){
}
}

public class ConstructorDemo4 {
public static void main(String[] args) throws Exception {
//得到所有的构造器(先得到类)
Class<Emp> c = Emp.class;
/**
* Constructor<?>[] getConstructors()
返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。
*/
Constructor[] con = c.getConstructors();//前面的修饰符必须是public才可以在这个方法下获取到
for (Constructor cons : con) {
System.out.println("c.getConstructors()"+cons);//如果上面的某构造器public去掉,则显示不出
/**打印
public junereflect624.Emp(java.lang.String,int)
*/
}

//得到指定的构造器,也是必须public
Constructor c1 = c.getConstructor(String.class,int.class);
System.out.println(c1);//public junereflect624.Emp(java.lang.String,int)

System.out.println("====================================");
//现在想获得不受public影响的,getDeclaredConstructors(),暴力反射

con = c.getDeclaredConstructors();
for (Constructor cons : con) {

System.out.println("c.getDeclaredConstructors()=="+cons);//此时不受修饰符的影响
/**打印
* public junereflect624.Emp()
public junereflect624.Emp(java.lang.String)
public junereflect624.Emp(java.lang.String,int)
*/
}
}
}


 

利用Class获取类的方法Method

 

     Method类用于描述类中的方法

     

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

      返回该Class对象表示类和其父类的指定的public方法;

 

      Method[] getMethods(): 

      返回该Class对象表示类和其父类的所有public方法;

 

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

      返回该Class对象表示类的指定的方法。和访问权限无关,但不包括继承的方法;

  

       Method[] getDeclaredMethods():

      返回类所有的方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法;

 

例子:

 

class AB{
protected String name;
protected String id;
}
@Deprecated
public class MethodDemo5 extends AB{
void show(){}
public void say(){}
private int age;
public char c;
private boolean b;
public static void main(String[] args) throws Exception {
Class<MethodDemo5> c = MethodDemo5.class;
//获取所有的(包含父类的方法)public修饰的方法
Method[] m = c.getMethods();
for (Method method : m) {
System.out.println(method);
}

//获取指定的方法
Method me = c.getMethod("main", String[].class);
System.out.println("main "+me);//main public static void junereflect624.MethodDemo5.main(java.lang.String[]) throws java.lang.Exception

//访问所有方法,不受访问权限影响
m = c.getDeclaredMethods();
for (Method method : m) {
System.out.println("不受影响的:"+method);
}

me = c.getDeclaredMethod("show");
System.out.println(me);//void junereflect624.MethodDemo.show()

me = c.getMethod("toString");
System.out.println(me);//public java.lang.String java.lang.Object.toString()

/**
* Method[] getDeclaredMethods()
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,
包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法,只可以对当前类有效
*/
/*me = c.getDeclaredMethod("toString");//ERROR,c.getDeclaredMethod()不能得到继承的方法
System.out.println(me);//public java.lang.String java.lang.Object.toString()
*/
//得到字段
Field[] f = c.getFields();
for (Field field : f) {//只得到了public的
System.out.println("字段"+field);
}

//特定字段
Field fi = c.getField("c");//""里面是名称
System.out.println(fi);//public char junereflect624.MethodDemo.c

//得到不受限定名限定的全部字段
f = c.getDeclaredFields();
for (Field field : f) {//得到不受修饰符限定的字段,但是只对当前类有效
System.out.println("全部字段:"+field);
/**
* 全部字段:private int junereflect624.MethodDemo.age
全部字段:public char junereflect624.MethodDemo.c
全部字段:private boolean junereflect624.MethodDemo.b
*/
}
//注释 Annotation
Annotation[] a = c.getAnnotations();
System.out.println(a.length);
for (Annotation annotation : a) {
System.out.println(annotation);
}

//特定注解
Deprecated d = c.getAnnotation(Deprecated.class);
System.out.println(d);
}
}



利用Class获取字段字段Field

 

class Stu{
public String name;
public String sex;
public int age;

public Stu(String name, String sex, int age) {
super();
this.name = name;
this.sex = sex;
this.age = age;
}
}

public class ReflectDemo6 {
public static void main(String[] args) throws Exception {
Stu s = new Stu("ll", "男", 22);

Class<Stu> c = Stu.class;

Field f = c.getField("name");
System.out.println(f.get(s));////从哪个对象身上取
//修改对象的值
/**
Field f = c.getField("name");
f.set(s,"fff");
System.out.println(f.get(s));//从哪个对象身上取
*/
}
}


 

利用反射创建对象

   

  1、使用Class对象的newInstance()方法创建该Class对象的实例,此时该Class对象必须要有无参数的构造方法。

  2、使用Class对象获取指定的Constructor对象,再调用Constructor的newInstance()方法创建对象类的实例,此时可以选择使用

 某个构造方法。如果这个构造方法被私有化起来,那么必须先申请访问,将可以访问设置为true;

 例子:

 

class User{
/*private User(){//将默认的构造方法私有化的话就不可以再创建对象,两种方法都是这样
}*/
public String toString() {
return "User对象创建成功!";
}
}

public class NewInstanceDemo6 {
public static void main(String[] args) throws Exception {
//传统方式创建对象
System.out.println(new User());

//使用反射的方式
Class<User> c = User.class;
User u = c.newInstance();(直接newInstance的话必须保证默认的构造方法正常存在,也就是没有被私有化!这是前提条件)
System.out.println(u);
}
}


 

class Per{
private String name;
private int age;
private Per(){
}
private Per(String name){
}
public String toString() {
return "对象!!!";
}
}

public class NewInstanceDemo7 {
public static void main(String[] args) throws Exception {
Class<Per> c = Per.class;
//System.out.println(c.newInstance());;//证明利用无参的可以

////先获得需要被调用的构造器(private 修饰的构造方法)
Constructor<Per> con = c.getDeclaredConstructor();//调用默认的,什么都不要写
System.out.println(con);//private junereflect624.Per()
/*con = c.getDeclaredConstructor(String.class);获取指定的构造方法
System.out.println(con);//private junereflect624.Per(java.lang.String)*/
//现在只需要执行这个构造器,
con.setAccessible(true);//允许访问
Per p = con.newInstance();//成功,通过私有的受保护的构造方法创建了对象
System.out.println("无参构造方法"+p);

con = c.getDeclaredConstructor(String.class);
System.out.println(con);//private junereflect624.Per(java.lang.String)

con.setAccessible(true);//允许访问
p = con.newInstance("liuzhao");//成功,通过私有的受保护的构造方法创建了对象
System.out.println("String构造方法"+p);
}
}
package junereflect624;

import java.lang.reflect.Constructor;

enum Color{
RED,BLUE,GREEN;
private Color(){
}
}

public class EnumDemo8 {
public static void main(String[] args) throws Exception {
Class<Color> c = Color.class;

Constructor<Color> con = c.getDeclaredConstructor();//(错误在这一行发生,就是说对枚举而言这种方法连构造器都获得不了,)编译可以通过,但是运行就通不过了!
Color co = (Color) con.newInstance();
System.out.println(co);//失败,证明对枚举而言不行,所以枚举的单例模式更加安全
System.out.println(c.isEnum());//true是枚举
}
}



利用反射调用方法

  

 每个Method的对象对应一个具体的底层方法。获得Method对象后,程序可以使用Method里面的invoke方法来执行该底层方法。

 Object invoke(Object obj,Object ... args):obj表示调用底层方法的对象,后面的args表示传递的实际参数


例子:

 

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

/*传统方式:
String name = new Dept().show("hello world");
System.out.println(name);*/

/**
* Method getMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
name - 方法名
parameterTypes - 参数列表
*/
//想要通过反射来调用Dept中的方法
Class<Dept> c = Dept.class;
Method m = c.getMethod("show", String.class);
Object o = m.invoke(c.newInstance(), "hello world");
System.out.println(o);

//私有化的方法
m = c.getDeclaredMethod("privateshow");//无参方法
m.setAccessible(true);
o = m.invoke(c.newInstance());

//静态方法的调用
m = c.getMethod("staticshow");
m.invoke(null);//staticshow为静态方法,不需创建对象,所以这里会是null
}
}


实现一个小框架,通过配置文件获取要调用的类名

class ReflecTest2 {
public static void main(String[] args) throws Exception {
// 应该先直接用ArrayList和HashSet,然后才引入从配置文件读,这样便于学员学习。
Properties props = new Properties();
// 先演示相对路径的问题
// InputStream ips = new FileInputStream("config.properties");
/*
* 一个类加载器能加载.class文件,那它当然也能加载classpath环境下的其他文件,既然它有如此能力,它没有理由不
* 顺带提供这样一个方法。它也只能加载classpath环境下的那些文件。注意:直接使用类加载器时,不能以/打头。
*/
// InputStream ips =
// ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/javaenhance/config. //properties");
// Class提供了一个便利方法,用加载当前类的那个类加载器去加载相同包目录下的文件
// InputStream ips =
// ReflectTest2.class.getResourceAsStream("config.properties");
InputStream ips = ReflectTest2.class.getResourceAsStream("/cn/itcast/javaenhance/config.properties");
props.load(ips);
ips.close();

String className = props.getProperty("className");
Class clazz = Class.forName(className);

Collection collection = (Collection) clazz.newInstance();
// Collection collection = new ArrayList();
ReflectPoint pt1 = new ReflectPoint(3, 3);
ReflectPoint pt2 = new ReflectPoint(5, 5);
ReflectPoint pt3 = new ReflectPoint(3, 3);
collection.add(pt1);
collection.add(pt2);
collection.add(pt3);
collection.add(pt1);
System.out.println(collection.size());
}
}