JavaSE(十)之反射详解编程语言

开始接触的时候可能大家都会很模糊到底什么是反射,大家都以为这个东西不重要,其实很重要的,几乎所有的框架都要用到反射,增加灵活度。到了后面几乎动不动就要用到反射。

首先我们先来认识一下对象

学生—–>抽象—–>Student

表示学生   Student = …….

那我们的反射中的Class呢?

类型—–>抽象—–>Class(反射的入口破)

java.lang.class    表示java中的类型

  Class c = Student.Class

  Class c = int.Class

  Class c =int[].Class

一、反射(Reflection)的概述

1.1、定义

  反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法 对于任意一个对象,都能够调用它的任意一个方法和属性, 这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

  即:在”运行时”,通过反射机制可以动态获得到和该类型相关的各种信息。    

  java通常是先有类后有对象,有对象就可以调用方法或属性。反射其实是通过Class对象来调用类里面的方法。
  通过反射可以调用私有方法和私有属性。大部分框架都是运用反射原理。

1.2、Class类型     java.lang.Class类

  Class是对java中所有类型的抽象。即一个Class类型对象可以表示出java中任意一种类型。每种类型在加载到内存后,内存中都会生产一个与之对应的Class类型对象(有且只有一个),用来表示该类型。

  每个类型都有且只有一个Class类型对象与之对应,通过这个Class类型对象就可以获得到该类型中的各种信息。Class类是Java反射的入口.。

  1)表示基本类型
              Class c = int.class;
              System.out.println(c.isPrimitive());//true
              System.out.println(c.getName());//int
            
              注:其他基本类型的情况类似
  2)表示类类型
              注:s.getClass()方法返回的是变量s所指向对象的实现类型的Class对象。
              Student s = new Student();
              Class c1 = s.getClass();
              Class c2 = Student.class;
              System.out.println(c1 == c2);//true
            
              //p指向的对象实际类型是Student
              Person p = new Student();
              Class c1 = p.getClass();//c1表示Student类型
              Class c2 = Person.class;//c2表示Person类型
              System.out.println(c1 == c2);//false
  3)表示接口类型
              Action a = new Student();
              Class c1 = a.getClass();//c1表示Student类型
              Class c2 = Action.class;//c2表示Action类型
              System.out.println(c1 == c2);//false
              System.out.println(c2.isInterface());//true
        
  4)表示数组类型
              int[] a = new int[4];
              Class c1 = a.getClass();
              Class c2 = int[].class;
              System.out.println(c1 == c2);//true
              System.out.println(c1.isArray());//true

              Class c3 = c1.getComponentType();//c3表示该数组是使用什么类型声明出来的
              System.out.println(c3.getName());//int
           
              Student[] a = new Student[4];
              Class c1 = a.getClass();
              Class c2 = Student[].class;
              System.out.println(c1 == c2);//true
              System.out.println(c1.isArray());//true

              Class c3 = c1.getComponentType();//c3表示该数组是使用什么类型声明出来的
              System.out.println(c3.getName());//com.briup.test.Student

1.3、获取一个类类型的Class对象的三种方式

  想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到

  我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象

    1)使用Class类中的forName方法获得

          Class clazz1 = Class.forName(“全限定类名”);  //通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。

          这种方法很灵活,只需一个String类型参数即可,而String类型的数据改变起来很容易,注意该方法是会抛出异常。

    2)使用类名获得

        Class clazz2  = Person.class;    //当类被加载成.class文件时,此时Person类变成了.class,在获取该字节码文件对象,也就是获取自己, 该类处于字节码阶段。

    3)使用对象调用getClass方法获得    

        Class clazz3 = p.getClass();    //通过类的实例获取该类的字节码文件对象,该类处于创建对象阶段。

          getClass是Object中的final修饰的方法,每个对象都可以调用而且不能重写 

    注:以上三种方法获得的同一个对象(==比较),因为每个类型内存都有且只有一个Class类型对象。

1.4、反射机制中的常见类的含义

  java.lang包下:
    Class  类        对java中所有类型抽象而得来的
    Package类        对java中所有包抽象而得来的

  java.lang.reflect包下:
    Modifier    类    对java中所有修饰符抽象而得来的
    Field        类    对java中所有属性抽象而得来的
    Method        类    对java中所有方法抽象而得来的
    Constructor 类    对java中所有构造器抽象而得来的
    Array        类    提供了对数组对象的动态访问
    ParameterizedType接口  在反射中表示参数化类型
    例如:List<String> Point<Long,Long>等这种带泛型的类型

二、反射机制获取类中的信息

2.1、使用Class类型对象获得类中的信息

JavaSE(十)之反射详解编程语言

1.获得该类所处的包的信息 
Student s = new Student(); 
Class c = s.getClass(); 
System.out.println(c.getPackage().getName()); 
2.获得该类的修饰符信息 
//每个修饰符对应一个int值 
//如果有多个修饰符则int值相加 
Student s = new Student(); 
Class c = s.getClass(); 
System.out.println(c.getModifiers()); 
System.out.println(Modifier.PUBLIC); 
System.out.println(Modifier.FINAL); 
3.获得该类的名字 
Student s = new Student(); 
Class c = s.getClass(); 
System.out.println(c.getName()); 
4.获得该类的父类的Class对象 
Student s = new Student(); 
Class c = s.getClass(); 
//superclass表示其父类型的Class对象 
Class superclass = c.getSuperclass(); 
System.out.println(superclass.getName()); 
例如: 
Class c = Object.class; 
Class superclass = c.getSuperclass(); 
System.out.println(superclass.getName()); 
//运行报错,因为Object没有父类 
             
例如: 
//判断c1是不是c2的子类型 
//判断c3是不是c2的子类型 
Class c1 = Student.class; 
Class c2 = Person.class; 
Class c3 = String.class 
System.out.println(c2.isAssignableFrom(c1));//true 
System.out.println(c2.isAssignableFrom(c3));//false 
5.获得该类所实现的接口类型的Class对象 
Student s = new Student(); 
Class c = s.getClass(); 
Class[] interfaces = c.getInterfaces(); 
for(Class clazz:interfaces){ 
System.out.println(clazz.getName()); 
} 
例如: 
//判断c1是不是c2的实现类 
//判断c3是不是c2的实现类 
Class c1 = Student.class; 
Class c2 = Action.class; 
Class c3 = String.class 
System.out.println(c2.isAssignableFrom(c1));//true 
System.out.println(c2.isAssignableFrom(c3));//false 
6.获得该类中所有的属性 
Student s = new Student(); 
Class c = s.getClass(); 
Field[] declaredFields = c.getDeclaredFields(); 
for(Field f:declaredFields){ 
System.out.println(f.getModifiers()); 
System.out.println(f.getType().getName()); 
System.out.println(f.getName()); 
} 
注: 
getDeclaredFields()方法返回类中声明的属性,包括私有的 
getFields()方法只返回类中public修饰的属性,包括继承的 
例如: 
//获得某个指定的属性(也包括私有属性) 
Student s = new Student(); 
Class c = s.getClass(); 
Field f = c.getDeclaredField("score"); 
System.out.println(f.getModifiers()); 
System.out.println(f.getType().getName()); 
System.out.println(f.getName()); 
7.获得该类中所有的方法 
Student s = new Student(); 
Class c = s.getClass(); 
Method[] declaredMethods = c.getDeclaredMethods(); 
for(Method m:declaredMethods){ 
System.out.println(m.getModifiers()); 
System.out.println(m.getReturnType().getName()); 
System.out.println(m.getName()); 
System.out.println(Arrays.toString(m.getParameterTypes())); 
System.out.println(Arrays.toString(m.getExceptionTypes())); 
} 
注: 
getDeclaredMethods()方法返回类中声明的方法,包括私有的 
getMethods()方法只返回类中public修饰的方法,包括继承的 
例如: 
//获得某个指定的方法(也包括私有方法) 
Student s = new Student(); 
Class c = s.getClass(); 
Method m = c.getDeclaredMethod("print"); 
System.out.println(m.getModifiers()); 
System.out.println(m.getReturnType().getName()); 
System.out.println(m.getName()); 
System.out.println(Arrays.toString(m.getParameterTypes())); 
System.out.println(Arrays.toString(m.getExceptionTypes())); 
8.获得该类中所有的构造器 
Student s = new Student(); 
Class c = s.getClass(); 
Constructor[] declaredConstructors = c.getDeclaredConstructors(); 
for(Constructor con:declaredConstructors){ 
System.out.println(con.getModifiers()); 
System.out.println(con.getName()); 
System.out.println(Arrays.toString(con.getParameterTypes())); 
System.out.println(Arrays.toString(con.getExceptionTypes())); 
} 
注: 
getDeclaredConstructors()方法返回类中所有构造器 
getConstructors()方法只返回类中public修饰的构造器 
例如: 
//获得某个指定的构造器(也包括私有构造器) 
Student s = new Student(); 
Class c = s.getClass(); 
Constructor con = c.getDeclaredConstructor(double.class); 
System.out.println(con.getModifiers()); 
System.out.println(con.getName()); 
System.out.println(Arrays.toString(con.getParameterTypes())); 
System.out.println(Arrays.toString(con.getExceptionTypes())); 
9.获得父类型中的泛型的真实类型 
因为泛型类的泛型参数在编译期会被擦除,所以我们不能再运行期间直接拿到该泛型的实际类型,但是可以通过子类的Class对象来获取父类的泛型类型。 
例如: 不通过子类不能获得泛型实际类型 
public class GenericTest<T,S>{ 
public T name; 
public S say(T t,S s){ 
return s; 
} 
} 
main: 
GenericTest<String,Integer> t = new GenericTest<String,Integer>(); 
Class c = t.getClass(); 
Field field = c.getDeclaredField("name"); 
System.out.println(field.getType()); 
System.out.println(field.getGenericType()); 
//输出结果: 
class java.lang.Object 
T 
System.out.println("-------------------------"); 
Method method = c.getMethod("say", Object.class,Object.class); 
System.out.println(method.getReturnType()); 
System.out.println(method.getGenericReturnType()); 
//输出结果: 
class java.lang.Object 
S 
System.out.println("-------------------------"); 
System.out.println(Arrays.toString(method.getParameterTypes())); 
System.out.println(Arrays.toString(method.getGenericParameterTypes())); 
//输出结果: 
[class java.lang.Object, class java.lang.Object] 
[T, S] 
例如: 通过子类可以获得父类中泛型的实际类型 
public class GenericTest<T,S>{ 
public T name; 
public S say(T t,S s){ 
return s; 
} 
} 
public class Sub entends GenericTest<String,Integer>{} 
main: 
Class c = Sub.class; 
//获得父类类型,包含泛型参数信息  
Type superType = c.getGenericSuperclass(); 
//判断父类类型是不是属于ParameterizedType类型 
//ParameterizedType表示带泛型的类型 
if(superType instanceof ParameterizedType){ 
//强转,并调用方法获得泛型参数的实例类型 
ParameterizedType pt = (ParameterizedType)superType; 
Type[] actualTypeArguments = pt.getActualTypeArguments(); 
//循环遍历,并强转为Class类型,因为Type接口中没有任何方法 
for(Type t:actualTypeArguments){ 
Class clazz = (Class)t; 
System.out.println(clazz.getName()); 
} 
} 
例如: 通过子类可以获得实现接口中泛型的实际类型         
Class c = Sub.class; 
Type[] types = c.getGenericInterfaces(); 
for(Type t:types){ 
if(t instanceof ParameterizedType){ 
ParameterizedType pt = (ParameterizedType)t; 
Type[] actualTypeArguments = pt.getActualTypeArguments(); 
for(Type type:actualTypeArguments){ 
Class clazz = (Class)type; 
System.out.println(clazz.getName()); 
} 
} 
} 
10.获得类中的注解 
使用反射也可以获得类中的注解.(在之后的内容在来了解)

Class类型获取对象类中信息

2.2、反射的常用操作

 1 public class Student{  2             private long id;  3             private String name;  4   5             private static int age;  6               7             get/set  8   9             public static void say(){ 10                 System.out.println("say.."); 11             }

  1)使用反射的方式调用构造器创建类的对象

    默认方式:必须调用无参构造器
        Class c = Student.class;
        Student s = (Student)c.newInstance();


             
   通用方式:获得构造器对象,并调用该构造器

                注:getConstructor方法和newInstance方法的参数都是可变参数
                例如:获得无参构造器并调用创建对象
                Class c = Student.class;
                Constructor constructor = c.getConstructor();
                Student o = (Student)constructor.newInstance();
                System.out.println(o);

                例如:获得有参构造器并调用创建对象
                Class c = Student.class;
                Constructor constructor = c.getConstructor(long.class,String.class);
                Student o = (Student)constructor.newInstance(1L,”tom”);
                System.out.println(o);

  2)使用反射的方式访问对象中的属性
                例如:
                Student s = new Student();
                Class c = s.getClass();
                Field[] declaredFields = c.getDeclaredFields();
                for(Field f:declaredFields){

                    //设置私有属性可以被访问
                    f.setAccessible(true);

                    //判断属性是否为static,静态属性的访问不需要对象
                    if(Modifier.isStatic(f.getModifiers())){
                        System.out.println(f.getName() +” = “+f.get(null));
                    }else{
                        System.out.println(f.getName() +” = “+f.get(s));
                    }
                }

                注:Field类中的get方法可以获得属性值,set方法可以给属性设置值。
     
  3)使用反射的方式调用对象中的方法
                例如:
                Student s = new Student();
                Class c = s.getClass();
            
                Method m1 = c.getMethod(“setName”,String.class);
                //m1表示Student中的setName方法
                //调用对象s中的m1方法,并且传参”tom”
                //s.setName(“tom”);
                m1.invoke(s, “tom”);
            
                Method m2 = c.getMethod(“getName”);
                String name = (String)m2.invoke(s);
                System.out.println(“name = “+name);
            
                //调用静态方法 不需要对象
                Method m3 = c.getMethod(“say”);
                m3.invoke(null);

  4)使用反射的方式动态操作数组
                注:
                Object[] o1 = new int[1];//编译报错
                long[]      o2 = new int[1];//编译报错
                int[]     o3 = new int[1];//编译通过
            
                Object[] o1 = new 任意引用类型[1];//编译通过

                例如:

JavaSE(十)之反射详解编程语言

  //要求:传任意类型"数组",把数组长度扩大1倍并返回 
       //注意这里不能收Object[], 
      //因为Object[] o = new Integer[4];编译通过 
      //但是Object[] o = new int[4];      编译报错 
      //那么使用Object类中就可以接收任意类型的数组了 
       public static Object arrayCopy(Object obj){ 
         //代码 
                } 
      实现: 
       public static Object arrayCopy(Object obj){ 
     Class c = obj.getClass(); 
    Object newArray = null; 
     if(c.isArray()){ 
        int len = Array.getLength(obj); 
      Class<?> type = c.getComponentType(); 
     newArray = Array.newInstance(type, len*2); 
     for(int i=0;i<len;i++){ 
        Object value = Array.get(obj, i); 
         Array.set(newArray, i, value); 
        } 
         } 
        return newArray; 
       } 

数组长度扩大一倍

三、Class的API详解

3.1、通过字节码对象创建实例对象

         JavaSE(十)之反射详解编程语言

 

3.2、获取指定构造器方法

  constructor 如果没有无参构造,只有有参构造如何创建实例呢?看下面

 

         JavaSE(十)之反射详解编程语言

 

  总结上面创建实例对象:Class类的newInstance()方法是使用该类无参的构造函数创建对象, 如果一个类没有无参的构造函数, 就不能这样创建了,可以调用Class类的getConstructor(String.class,int.class)方法

    取一个指定的构造函数然后再调用Constructor类的newInstance(“张三”,20)方法创建对象

  获取全部构造方法  

 

         JavaSE(十)之反射详解编程语言     

3.3、获取成员变量并使用 

  Field对象

    获取指定成员变量

 

         JavaSE(十)之反射详解编程语言 

 

         Class.getField(String)方法可以获取类中的指定字段(可见的), 如果是私有的可以用getDeclaedField(“name”)方法获取,通过set(obj, “李四”)方法可以设置指定对象上该字段的值,

          如果是私有的需要先调用setAccessible(true)设置访问权限,用获取的指定的字段调用get(obj)可以获取指定对象中该字段的值

 

    获取全部成员变量

 

        JavaSE(十)之反射详解编程语言

3.4、获得方法并使用 

           Method

 

         JavaSE(十)之反射详解编程语言 

 

        Class.getMethod(String, Class…) 和 Class.getDeclaredMethod(String, Class…)方法可以获取类中的指定方法,    

          如果为私有方法,则需要打开一个权限。setAccessible(true);

        用invoke(Object, Object…)可以调用该方法,

        跟上面同理,也能一次性获得所有的方法

 

         JavaSE(十)之反射详解编程语言             

3.5、获得该类的所有接口

  Class[] getInterfaces():确定此对象所表示的类或接口实现的接口

  返回值:接口的字节码文件对象的数组

3.6、获取指定资源的输入流

  InputStream getResourceAsStream(String name)  

  return:一个 InputStream 对象;如果找不到带有该名称的资源,则返回 null

  参数:所需资源的名称,如果以”/”开始,则绝对资源名为”/”后面的一部分。

四、两个实例来全面了解反射

实例1:

JavaSE(十)之反射详解编程语言

package corejava.test; 
import java.lang.reflect.Array; 
import java.lang.reflect.Constructor; 
class A{ 
private int id; 
private String name="Tom"; 
private  A(int a,String name){ 
id=a; 
} 
@Override 
public String toString() { 
return "A [id=" + id + ", name=" + name + "]"; 
} 
} 
public class Test1 { 
public static Object createObject(String classname) throws Exception { 
Class<?> name = Class.forName(classname); 
return createObject(name); 
} 
public static Object createObject(Class<?>c) throws Exception{ 
Object object=null; 
try { 
object=c.newInstance(); 
} catch (InstantiationException e) { 
//            如果此 Class 表示一个抽象类、接口、数组类、基本类型或 void;  
//            或者该类没有 null 构造方法; 或者由于其他某种原因导致实例化失败。 
//            2、数组类、基本类型  能够创建对象 
if(c.isArray()){ 
object=Array.newInstance(c.getComponentType(),5); 
} 
else if(c.isPrimitive()){ 
if(c == boolean.class){ 
object=false; 
}else if(c==void.class){ 
throw new Exception(c.getName()+"不能创建对象!"); 
}else { 
object=0; 
} 
}else { 
//                3、没有 null 构造方法 
                 
Constructor<?>[] constructors = c.getConstructors(); 
if(constructors.length==0) throw new Exception(c.getName()+"没有Public构造器!"); 
Class<?>[] parameterTypes = constructors[0].getParameterTypes(); 
Object[] parameters=new Object[parameterTypes.length]; 
for(int i=0;i<parameters.length;i++){ 
parameters[i]=createObject(parameterTypes[i]); 
} 
object=constructors[0].newInstance(parameters); 
} 
//            1、其他某种原因、void、抽象类、接口时抛出异常 
if(object==null)throw new Exception(c.getName()+"不能创建对象!"); 
} 
return object; 
} 
public static void main(String[] args) throws Exception { 
System.out.println(createObject(A.class)); 
System.out.println("ssss"); 
//        System.out.println(new String()); 
//        System.out.println("ssss"); 
    } 
}

实例1

实例2:

JavaSE(十)之反射详解编程语言

package corejava.test; 
import java.lang.reflect.Constructor; 
import java.lang.reflect.Field; 
import java.lang.reflect.Method; 
import java.lang.reflect.Modifier; 
import java.lang.reflect.Parameter; 
import java.nio.file.spi.FileSystemProvider; 
import static java.lang.System.out; 
final class B{ 
private int id; 
private String name="Tom"; 
private static String mm="Tom"; 
private  B(int a,String name){ 
id=a; 
} 
@Override 
public String toString() { 
return "B [id=" + id + ", name=" + name + "]"; 
} 
public int f(String name,A a){ 
return id;} 
} 
public class Test2 { 
public static void printClass(Class<?> c) throws Exception{ 
//1、包名; 
out.println(c.getPackage()+";"); 
//2、修饰符 +class+类名{ 
out.println(Modifier.toString(c.getModifiers())+ 
" class "+c.getSimpleName()+" {"); 
//3、修饰符 +属性类型+属性名; 
for(Field f:c.getDeclaredFields()){ 
out.print("/t"+Modifier.toString(f.getModifiers())+ 
" "+f.getType().getSimpleName()+" "+f.getName()); 
f.setAccessible(true); 
if(Modifier.isStatic(f.getModifiers())) 
out.print(" = "+f.get(null)); 
out.println(";"); 
} 
//4、修饰符 +类名+( 参数类型+参数名字){  } 
for(Constructor<?> constructor:c.getDeclaredConstructors()){ 
out.print("/t"+Modifier.toString(constructor.getModifiers())+" "+ 
c.getSimpleName()+"(");     
Parameter[] ps = constructor.getParameters(); 
for(int i=0;i<ps.length;i++){ 
out.print(ps[i].getType().getSimpleName()+" " 
+ps[i].getName()); 
if(i<ps.length-1)out.print(","); 
} 
out.println("){}"); 
} 
out.println(); 
//5、修饰符 +返回类型+方法名++( 参数类型+参数名字){  } 
for(Method m:c.getDeclaredMethods()){ 
out.print("/t"+Modifier.toString(m.getModifiers())+" "+ 
m.getReturnType().getSimpleName()+" "+ 
m.getName()+"(");     
Parameter[] ps = m.getParameters(); 
for(int i=0;i<ps.length;i++){ 
out.print(ps[i].getType().getSimpleName()+" " 
+ps[i].getName()); 
if(i<ps.length-1)out.print(","); 
} 
out.println("){}"); 
out.println(); 
} 
//6、} 
out.println("}"); 
} 
public static void main(String[] args) throws Exception { 
printClass(Object.class); 
} 
}

实例2

 

觉得不错的“点个推荐”哦!

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/12415.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论