java基础知识—Java中的反射机制,
java基础知识—Java中的反射机制,
1.java反射机制的定义
首先,让我们来看看度娘是怎么定义反射机制的:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
2.java反射机制的优点和缺点
为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念
静态编译:在编译时确定类型,绑定对象,即通过。
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。
一句话,反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。
它的缺点是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。 1,丧失了编译时类型检查的好处 2,执行反射访问所需代码笨拙冗长 3,性能损失
对于特定的复杂的系统编程任务,反射很有必要,如果编写的程序必须要与编译时未知的类一起工作,就可以用反射仅仅来实例化对象,但不应该通过反射来访问对象,应该通过被实例化对象的接口或者父类来访问对象的方法,这就是接口优先于反射机制的含义。
3.java中的Class类
在讲反射机制之前,我们不得不先了解一下Class类。
在程序运行期间,Java运行时系统始终未所有对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。这些信息可以通过专门的Java类来访问,而保存这些信息的类被称为Class类。(Class类是封装类型信息的类,每个类都有一个Class对象(保存在一个同名的.class文件中),每个java类运行时都在JVM里表现为一个class对象,当装载类时,Class类型的对象自动创建。某个类的Class对象被载入内存,它就用来创建这个类的所有对象。)
Class类不是我们人为创建的,是由java虚拟机在我们生成.class文件的时候创建的,我们可以通过几种方法,获得这个Class类实例。下面介绍一下这几种方法:
(1)利用对象调用getClass()方法获取该对象的Class实例;
(2)使用Class类的静态方法forName(),用类的名字获取一个Class实例;
例如我们在连接Mysql数据时,就会用到:
Class.forName("com.mysql.jdbc.Driver");//加载Mysql驱动器</span>
面试题:Class.forName的作用:
forName也是返回这个类(java.lang.String)的字节码,返回的方式分为两种情况:
1.这个类的字节码已经加载到内存中,此时想要得到它的字节码,不需要再加载,而是找到这个字节码,将字节码返回。
2.这个类的字节码还没有加载到内存中,首先用类加载器加载,加载进来以后将就字节码在虚拟机中缓存起来,同时forName方法返回刚才加载进来的字节码。
(3)运用.class的方式来获取Class实例,对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例。
public class Test {
public static void main(String[] args){
Date date=new Date();
Class cl=date.getClass();//(1)调用.getClass()方法
System.out.println(cl.getName());//output:java.util.Date
Class c1=Integer.TYPE;//(3)调用.TYPE
System.out.println(c1.getName());//output:int
Class cl1=int.class;//(3)调用.class
System.out.println(cl1.getName());//output:int
}
}
4.获取反射类的实例
Class类中有一个方法叫做newInstance(),它可以用来创建一个Class类对象的新实例。Class对象包含的内容就是反射好的那个类,我们要构造那个类的新实例(新对象)。
4.1Class类的无参构造对象
public class Demo {
public static void main(String[] args) {
//实例化Class对象,forName()方法会抛异常
Class<?> c = null;
try {
//这里需要完整的包名和类名
c = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//生成一个字符串的引用
String s = null;
try {
//将构造好的对象向下转型为String类
//newInstance()方法会抛异常
s = (String) c.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
System.out.println("字符串长度: " + s.length());
}
}
注意:因Class的newInstance是不接受参数的,只能利用默认构造函数。
4.2Class类的有参构造对象
import java.lang.reflect.Constructor;
public class Demo {
//下面的几个方法抛出来的异常太多,为了代码的紧凑性,这里就直接抛给虚拟机了
public static void main(String[] args) throws Exception {
Class<?> c = null;
try {
c = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
char[] ch = {'h','e','l','l','o'};
String s = null;
//获得Class类对象的有参构造方法,括号里面参数的写法是:类型.class
Constructor<?> con = c.getConstructor(char[].class);
//用此构造方法构造一个新的字符串对象,参数为一个char数组
s = (String) con.newInstance(ch);
System.out.println("构造的字符串:" + s);//output:hello
}
}
5.获取类的构造器
首先介绍一下Constructor类,这个类用来封装反射得到的构造器,Class有四个方法来获得Constructor对象
- public Constructor<?>[] getConstructors() 返回类中所有的public构造器集合,默认构造器的下标为0
- public Constructor<T> getConstructor(Class<?>... parameterTypes) 返回指定public构造器,参数为构造器参数类型集合
- public Constructor<?>[] getDeclaredConstructors() 返回类中所有的构造器,包括私有
- public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 返回任意指定的构造器
/**
* 获取构造方法Constructor
* getConstructor() only for public
* getDeclaredConstructor() global access all
*
* */
//指定参数列表获取特定的方法
Constructor con = cls1.getDeclaredConstructor(new Class[]{String.class});
con.setAccessible(true); //设置可访问的权限
Object obj = con.newInstance(new Object[]{"liyang"});
System.out.println(obj); //打印一下这个对象的信息
//获取所有的构造方法集合
Constructor con1[] = cls1.getDeclaredConstructors();
con1[1].setAccessible(true);
Object obj1 = con1[1].newInstance(new Object[]{"tom"});
System.out.println(obj1);
解释一下:第一个是获得一个指定的方法,我们指定了参数是一个String类型,第二段我们获得了所有的构造方法集合,并选取了其中一个创建了新的对象。注意,以上的四个方法全部需要抛出异常,当我们获得私有的方法的时候,要用setAccessible设置一下可访问的权限,例子中没有演示获取共有方法,那个比较简单,就不做介绍了,其实掌握了上面两个,其他就好理解了。
6.获取类的方法
我觉得你已经可以帮我写这一段了,封装类的方法的类是Method.获取method也有四个方法,猜到了没??- public Method[] getMethods() 获取所有的共有方法的集合
- public Method getMethod(String name,Class<?>... parameterTypes) 获取指定公有方法 参数1:方法名 参数2:参数类型集合
- public Method[] getDeclaredMethods() 获取所有的方法
- public Method getDeclaredMethod(String name,Class<?>... parameterTypes) 获取任意指定方法
import java.lang.reflect.Method;
class Person {
public void print(int i) {
System.out.println("我在写数字: " + i);
}
public static void say(String str) {
System.out.println("我在说: " + str);
}
}
public class Demo {
public static void main(String[] args) throws Exception {
Person p = new Person();
Class<?> c = p.getClass();
//getMethod()方法需要传入方法名,和参数类型
Method m1 = c.getMethod("print", int.class);
//invoke()表示调用的意思,需要传入对象和参数
m1.invoke(p, 10);
Method m2 = c.getMethod("say", String.class);
//这里的null表示不由对象调用,也就是静态方法
m2.invoke(null, "你妹");
}
}
7.获取类的成员变量
了解了构造器,其实你可以猜到成员变量的获取方法了,成员变量用Field类进行封装。 主要的方法非常的类似:- public Field getDeclaredField(String name) 获取任意指定名字的成员
- public Field[] getDeclaredFields() 获取所有的成员变量
- public Field getField(String name) 获取任意public成员变量
- public Field[] getFields() 获取所有的public成员变量
import java.lang.reflect.Field;
class Person {
public String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Demo {
public static void main(String[] args) throws Exception {
Person p = new Person("zhangsan",12);
Class<?> c = p.getClass();
//获取公共属性的值
Field f1 = c.getField("name");
//get(p)表明要获取是哪个对象的值
String str = (String) f1.get(p);
System.out.println("姓名: " + str);
//获取私有属性的值
Field f2 = c.getDeclaredField("age");
//age是私有属性,所以要设置安全检查为true
f2.setAccessible(true);
int age = (int) f2.get(p);
System.out.println("年龄: " + age);
}
}
相关文章
- 暂无相关文章
用户点评