Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
反射允许静态语言在运行时检查、修改程序的结构与行为。在静态语言中,使用一个变量时,必须知道它的类型。在Java中,变量的类型信息在编译时都保存到了class文件中,这样在运行时才能保证准确无误;换句话说,程序在运行时的行为都是固定的。如果想在运行时改变,就需要反射这东西了。总之:反射可以赋予jvm动态编译的能力,否则类的原数据信息只能用静态编译的方式实现
实现Java反射机制的类都位于:
java.lang.Class类时整个反射技术中最重要的对象,所有的其他反射对象都是通过Class类得到到,因此可以说Class是整个反射的源头。Class对象是外部访问该类对象的入口,在虚拟机加载完每一个类的同时都会对应生成一个相应的Class对象,它就像每个类的镜子一样,可以从该Class中获取到对应类的所有结构。获取Class实例的方法如下:
首先,先创建一个Person类方便演示:
package com.lys.java;
public class Person {
private String name;
public int age;
public Person(){
System.out.println("Person()");
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
// 私有构造方法
private Person(String name){
this.name = name;
}
public void show(){
System.out.println("你好,我是一个人");
}
private String showNation(String nation){
System.out.println("我的国籍是:" + nation);
return nation;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
测试:
package com.lys.java;
public class ReflectionTest {
public static void main(String[] args) throws Exception {
new ReflectionTest().test1();
}
// 获取Class的实例的方式:4种方式
public void test1() throws ClassNotFoundException{
//方式一:调用运行时类的属性:.class
Class<Person> clazz1 = Person.class;
System.out.println("方法一:" + clazz1);
//方式二:通过运行时类的对象,调用getClass()
Person p1 = new Person();
Class clazz2 = p1.getClass();
System.out.println("方法二:" + clazz2);
//方式三:调用Class的静态方法:forName(String classPath)【多】
// 形参str:指的是你所要获取Class所对应的类的全名
Class clazz3 = Class.forName("com.lys.java.Person");
System.out.println("方法三:" + clazz3);
System.out.println("方法一与方法二获取是否相同:" + clazz1 == clazz2);
System.out.println("方法一与方法三获取是否相同:" + clazz1 == clazz3);
//方式四:使用类的加载器:ClassLoader【了解】
ClassLoader classLoader = ReflectionTest.class.getClassLoader();
Class clazz4 = classLoader.loadClass("com.lys.java.Person");
System.out.println("方法四:" + clazz4);
}
}
方法一:class com.lys.java.Person
Person()
方法二:class com.lys.java.Person
方法三:class com.lys.java.Person
方法一与方法二获取是否相同:true
方法一与方法三获取是否相同:true
方法四:class com.lys.java.Person
【说明】:发现4种方法的结果都是相同的!
首先,回顾一下我们之前创建类对象的过程,然后,在熟悉一下反射的创建步骤:
// 原先创建类对象
public void test1(){
Person p1 = new Person("Tom", 12);
p1.age = 10;
System.out.println(p1.toString());
p1.show();
}
// 利用反射创建类对象
public void test2() throws Exception{
Class clazz = Person.class;
// 1.通过反射,创建Person类对象
Constructor cons = clazz.getConstructor(String.class, int.class);
Object obj = cons.newInstance("Tom", 12);
Person p = (Person) obj;
System.out.println(obj.toString());
// 2.通过反射,调用对象指定的属性与方法
// 2.1调用属性
Field age = clazz.getDeclaredField("age");
age.set(p,10);
System.out.println(obj.toString());
// 2.2调用方法
Method show = clazz.getDeclaredMethod("show");
show.invoke(p);
System.out.println("**************************************");
// 3.通过反射,可以调用Person类的私有结构
// 3.1调用私有构造方法
Constructor cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessible(true);
Person p1 = (Person) cons1.newInstance("ROY");
System.out.println(p1);
// 3.2调用私有属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p1, "ROY_NEW");
System.out.println(p1);
// 3.3调用私有方法
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
showNation.invoke(p1, "中国");
}
【输出】:
Person [name=Tom, age=10]
你好,我是一个人
Person [name=Tom, age=12]
Person [name=Tom, age=10]
你好,我是一个人
**************************************
Person [name=ROY, age=0]
Person [name=ROY_NEW, age=0]
我的国籍是:中国
【注意】
方法newInstance() 无参数时:创建对应运行时类的对象,内部调用了运行时类的空参构造器
通常在javabean中要求提供一个public的空参构造器的原因:
我们随机进行5次测试,来创建3种java中不同的类对象:
package com.lys.java;
import java.util.Random;
public class NewInstanceTest {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
new NewInstanceTest().test1();
}
// 随机测试
public void test1(){
for(int i = 1; i <= 5; i++ ){
int num = new Random().nextInt(3);
String classPath = "";
switch (num) {
case 0:
classPath = "java.util.Date";
break;
case 1:
classPath = "java.lang.Object";
break;
case 2:
classPath = "com.atguigu.java.Person";
break;
}
try {
System.out.println("-----第" + i + "次测试-----");
Object obj = getInstance(classPath);
System.out.println(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*
* 创建一个指定类的对象。
* classpath:指定类的全类名
* */
public Object getInstance(String classpath) throws Exception{
Class clazz = Class.forName(classpath);
return clazz.newInstance();
}
}
-----第1次测试-----
Person()
Person [name=null, age=0]
-----第2次测试-----
Fri Sep 11 11:00:15 CST 2020
-----第3次测试-----
java.lang.Object@5c647e05
-----第4次测试-----
Person()
Person [name=null, age=0]
-----第5次测试-----
Person()
Person [name=null, age=0]
【注意】:尽可能的创建更多权限的属性和方法进行练习。
新的Person类创建:
package com.lys.java1;
@MyAnnotation(value="hi")
public class Person extends Creature<String> implements Comparable<String>,MyInterface{
private String name;
int age;
public int id;
public Person(){}
@MyAnnotation(value="abc")
private Person(String name){
this.name = name;
}
Person(String name, int age){
this.name = name;
this.age = age;
}
@MyAnnotation
private String show(String nation){
System.out.println("我的国籍是:" + nation);
return nation;
}
public String display(String interests, int age) throws NullPointerException, ClassCastException{
return interests + age;
}
@Override
public void info(){
System.out.println("我是一个人");
}
@Override
public int compareTo(String o) {
return 0;
}
private static void showDesc(){
System.out.println("我是一个可爱的人");
}
}
Person父类的创建:
package com.lys.java1;
import java.io.Serializable;
public class Creature<T> implements Serializable {
private char gender;
public double weight;
private void breath(){
System.out.println("生物呼吸");
}
public void eat(){
System.out.println("生物吃东西");
}
}
定义一个接口:MyInterface
package com.lys.java1;
public interface MyInterface {
void info();
}
package com.lys.java1;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "hello";
}
反射获取对象属性时,常用的方法:
package com.lys.java2;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import com.lys.java1.Person;
public class FieldTest {
public static void main(String[] args) {
new FieldTest().test1();
}
public void test1(){
Class clazz = Person.class;
// 获取属性
// 1.getFields():获取当前运行时类及其父类中声明为public的访问权限属性
Field[] fields = clazz.getFields();
for(Field f : fields){
System.out.println(f);
}
System.out.println();
// 2.getDeclaredFields():获取当前运行时类中声明的所有属性(不包含父类中)
Field[] declaredfields = clazz.getDeclaredFields();
for(Field f : declaredfields){
System.out.println(f);
}
}
public void test2(){
Class clazz = Person.class;
Field[] declaredfields = clazz.getDeclaredFields();
for(Field f : declaredfields){
// 1.权限修饰符
int modifer = f.getModifiers();
// System.out.println(modifer + "--->" + Modifier.toString(modifer));
System.out.println(Modifier.toString(modifer));
// 2.数据类型
Class type = f.getType();
System.out.println(type.getName() + "\t");
// 3.变量名
String fName = f.getName();
System.out.println(fName);
System.out.println();
}
}
}
【测试1的输出】:
public int com.lys.java1.Person.id
public double com.lys.java1.Creature.weight
private java.lang.String com.lys.java1.Person.name
int com.lys.java1.Person.age
public int com.lys.java1.Person.id
【测试2的输出】:
private
java.lang.String
name
int
age
public
int
id
反射获取对象方法时,常用的方法:
package com.lys.java2;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import com.lys.java1.Person;
public class MethodTest {
public static void main(String[] args) {
new MethodTest().test1();
}
public void test1(){
Class clazz = Person.class;
//getMethods():获取当前运行时类及其父类中声明为public权限的方法
Method[] methods = clazz.getMethods();
for(Method m : methods){
System.out.println(m);
}
System.out.println();
//getDeclaredMethods():获取当前运行时类中声明的所有方法(不包含父类)
Method[] declaredmethods = clazz.getDeclaredMethods();
for(Method m : declaredmethods){
System.out.println(m);
}
}
public void test2(){
Class clazz = Person.class;
Method[] declaredmethods = clazz.getDeclaredMethods();
for(Method m : declaredmethods){
//1.获取方法声明的注解
Annotation[] annos = m.getAnnotations();
for(Annotation a : annos){
System.out.println(a);
}
//2.获取权限修饰符
System.out.print(Modifier.toString(m.getModifiers()) + "\t");
//3.返回值类型
System.out.print(m.getReturnType().getName() + "\t");
//4.方法名
System.out.print(m.getName());
System.out.print("(");
//5.形参列表
Class[] parameterTypes = m.getParameterTypes();
if(!(parameterTypes == null && parameterTypes.length == 0)){
for(int i = 0; i < parameterTypes.length; i++){
if(i == parameterTypes.length - 1){
System.out.print(parameterTypes[i].getName() + "args_" + i);
break;
}
System.out.print(parameterTypes[i].getName() + " args_ " + i + ",");
}
}
System.out.print(")");
//6.抛出的异常
Class[] exceptionTypes = m.getExceptionTypes();
if(exceptionTypes.length > 0){
System.out.print("throws");
for(int i = 0; i < exceptionTypes.length; i++){
if(i == exceptionTypes.length - 1){
System.out.print(exceptionTypes[i].getName());
}
System.out.print(exceptionTypes[i].getName() + ",");
}
}
System.out.println();
}
}
}
【测试1的输出】:
public int com.lys.java1.Person.compareTo(java.lang.String)
public int com.lys.java1.Person.compareTo(java.lang.Object)
public java.lang.String com.lys.java1.Person.display(java.lang.String,int) throws java.lang.NullPointerException,java.lang.ClassCastException
public void com.lys.java1.Person.info()
public void com.lys.java1.Creature.eat()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
public int com.lys.java1.Person.compareTo(java.lang.String)
public int com.lys.java1.Person.compareTo(java.lang.Object)
public java.lang.String com.lys.java1.Person.display(java.lang.String,int) throws java.lang.NullPointerException,java.lang.ClassCastException
private static void com.lys.java1.Person.showDesc()
private java.lang.String com.lys.java1.Person.show(java.lang.String)
public void com.lys.java1.Person.info()
【测试2的输出】:
public int compareTo(java.lang.Stringargs_0)
public volatile int compareTo(java.lang.Objectargs_0)
public java.lang.String display(java.lang.String args_ 0,intargs_1)throwsjava.lang.NullPointerException,java.lang.ClassCastExceptionjava.lang.ClassCastException,
@com.lys.java1.MyAnnotation(value=hello)
private java.lang.String show(java.lang.Stringargs_0)
public void info()
private static void showDesc()
package com.lys.java2;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import com.lys.java1.Person;
public class ReflectionTest {
public static void main(String[] args) throws Exception {
new ReflectionTest().testField2();
}
//【了解,不好】
public void testField() throws Exception{
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//获取指定的属性:要求运行时类中属性声明为public【不常用】
Field id = clazz.getField("id");
/*设置当前属性的值
方法set():
参数1:指明设置哪个对象的属性;
参数2:将此属性值设置为多少;
*/
id.set(p, 1001);
/*获取当前属性的值
方法get():
参数1:指明设置哪个对象的属性;
参数2:将此属性值设置为多少;
*/
int pId = (int) id.get(p);
System.out.println(pId);
}
//如何操作运行时类中的指定属性【重要】
public void testField1() throws Exception{
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
//1.getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
Field name = clazz.getDeclaredField("name");
//2.setAccessible():保证当前属性可访问
name.setAccessible(true);
//3.获取、设置指定对象是可访问的
name.set(p, "Tom");
System.out.println(name.get(p));
}
public void testField2() throws Exception{
Class clazz = Person.class;
//创建运行时类的对象
Person p = (Person) clazz.newInstance();
/*1.获取指定的某个方法:
* getDeclaredMethod():
* 参数1:方法名称;
* 参数2:指定方法的形参类型
*/
Method show = clazz.getDeclaredMethod("show", String.class);
show.setAccessible(true);
//2.调用方法
Object returnValues = show.invoke(p, "CHN");
System.out.println(returnValues);
System.out.println("********如何调用静态方法*********");
//private static void showDesc():静态方法
Method showDesc = clazz.getDeclaredMethod("showDesc");
showDesc.setAccessible(true);
Object returnVal = showDesc.invoke(Person.class);
System.out.println(returnVal);
}
}
【输出】:略
package com.lys.java2;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import com.lys.java1.Person;
public class OtherTest {
public static void main(String[] args) {
System.out.println("------------测试1------------");
new OtherTest().test1();
System.out.println("------------测试2------------");
new OtherTest().test2();
System.out.println("------------测试3------------");
new OtherTest().test3();
System.out.println("------------测试4------------");
new OtherTest().test4();
System.out.println("------------测试5------------");
new OtherTest().test5();
System.out.println("------------测试6------------");
new OtherTest().test6();
System.out.println("------------测试7------------");
new OtherTest().test7();
}
public void test1(){
Class clazz = Person.class;
//getConstructors():获取当前运行时类中声明为public的构造器
Constructor[] constructors = clazz.getConstructors();
for(Constructor c : constructors){
System.out.println(c);
}
System.out.println();
//getDeclaredConstructors():获取当前运行时类中声明的所有构造器
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for(Constructor c : declaredConstructors){
System.out.println(c);
}
}
// 获取运行时类的父类
public void test2(){
Class clazz = Person.class;
Class superclass = clazz.getSuperclass();
System.out.println(superclass);
}
// 获取运行时类的带泛型的父类
public void test3(){
Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);
}
// 获取运行时类的带泛型的父类的泛型
public void test4() {
Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
// 获取泛型类型
Type[] actualTypeArguments = paramType.getActualTypeArguments();
System.out.println(actualTypeArguments[0]);
System.out.println(actualTypeArguments[0].getTypeName());
System.out.println(((Class)actualTypeArguments[0]).getName());
}
// 获取运行时实现的接口
public void test5() {
Class clazz = Person.class;
Class[] interfaces = clazz.getInterfaces();
for(Class c : interfaces){
System.out.println(c);
}
System.out.println();
// 获取运行时类的父类实现的接口
Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
for(Class c : interfaces1){
System.out.println(c);
}
}
// 获取运行时类所在的包
public void test6() {
Class clazz = Person.class;
Package pack = clazz.getPackage();
System.out.println(pack);
}
// 获取运行时类声明的注解
public void test7() {
Class clazz = Person.class;
Annotation[] annotations = clazz.getAnnotations();
for(Annotation annos : annotations){
System.out.println(annos);
}
}
}
【输出】:
------------测试1------------
public com.lys.java1.Person()
com.lys.java1.Person(java.lang.String,int)
private com.lys.java1.Person(java.lang.String)
public com.lys.java1.Person()
------------测试2------------
class com.lys.java1.Creature
------------测试3------------
com.lys.java1.Creature<java.lang.String>
------------测试4------------
class java.lang.String
java.lang.String
java.lang.String
------------测试5------------
interface java.lang.Comparable
interface com.lys.java1.MyInterface
interface java.io.Serializable
------------测试6------------
package com.lys.java1
------------测试7------------
@com.lys.java1.MyAnnotation(value=hi)
反射的功能:
反射的优点:
反射的缺点:
我们知道,封装、继承、多态是Java的三大特性。而我们通过上面的哪些举例练习也能发现:通过反射,我们可以轻而易举的获取对私有成员的操作权利,那么这就是所谓的封装性遭受到破坏了吗?我们先来回忆一下“封装”是什么?
封装:简单理解就是将一个类的功能暴露给外部,但是将内部实现细节隐藏起来。换个说话就是类的外部只需要知道我给你提供了哪些功能即可,关于这些功能是是如何实现的你不需要知道,也不让你知道。java的封装机制就有效的在代码的编码阶段防止外部类窥探功能接口的内部原理和实现细节。
封装是在编码阶段的概念,而反射机制是在运行阶段的概念,有的时候人们为了想要了解类中更加细节的代码模块,可以通过反射的方式来了解,更加深刻的体会整个代码的工作过程。当然,也存在一些不遵守规矩的人,想通过反射获取所有操作权限进行恶意更改代码,或者寻找漏洞进行攻击,造成一定的安全隐患。
评论