您的位置:首页 > 其它

j2se学习中的一些零碎知识点9之反射

2017-11-30 12:17 363 查看
1、反射的一些基础知识:

反射的基本概念:反射的概念是由Smith在1982年首次提出,主要是指程序可以访问、检测和修改它本身状态和行为的一种能力,并根据自身行为的状态和结果,调整或者修改应用所描述行为的状态和相关的语义。Java中,反射是一种强大的工具,是能够创建灵活的代码,这些代码可以在运行时装配,无需在组建之间进行源代码链接。反射允许我们在编写与执行时,是我们的程序代码能够接入装载到JVM中的内部信息,而不是源代码中选定的类协作的代码。这使反射成为构建灵活的主要工具。但是需要注意的是,如果使用不当,反射的成本很高。

反射机制的优点:1、反编译:.class --> .java;2、通过反射机制访问java对象的属性,方法,构造方法等。

反射的两个缺点:
a、性能问题。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。用于字段和方法接入时反射要远远慢于直接代码。性能问题的程度取决于程序中是如何使用反射的。如果它作为程序运行中相对很少涉及的部分,缓慢的性能将不会是一个问题。
b、使用反射会模糊程序内部实际要发生的事情。程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术会带来维护问题。反射代码比相对应的直接代码更复杂。解决这些问题的最佳方案就是保守地使用反射--仅在于它可以真正增加灵活性的地方--记录其在目标类中的使用。

反射机制中主要需要掌握的类型:java.lang.Class;(可以认为代表.class文件) java.lang.reflect.Constructor; java.lang.reflect.Field; java.lang.reflect.Method; java.lang.reflect.Modifier;

获取Class类型对象的三种方式:

第一种方式:java.lang.Class类中的静态方法forName(String className)方法返回Class类型的对象,该对象代表的是整个类。(有时候必须使用类全名的字符串作为参数。)(forName静态方法是将某个.class文件装载到JVM中的过程,所以如果类中有静态语句块,静态语句块会执行。)



第二种方式:java中的每个类型都有class属性,如Class c = Employee.class;(不会执行Employee类中的静态语句块。)(需要注意,Class c = int.class; 即c代表int类型。)

第三种方式:java语言中任何一个java对象都有getClass方法,如Employee e = new Employee(); Class c = e.getClass();(java.lang.Object类中的getClass()方法,返回的是这个对象的运行时类。)



需要注意的是,在JVM中运行时类存在唯一,即 Class c1 = java.util.Date.class; Class c2 = Class.forName("java.util.Date"); System.out.println(c1 = c2); ,结果为true。

2、通过java.lang.Class类中的newInstance()方法创建由该类对象表示的类的新实例:(底层调用指定类中的无参数构造方法)(Class类可以使用泛型)



3、关于java中的可变长参数:(需要注意,可变长参数可以等同看作数组)

package com.geeklicreed.j2se;
/*
* 关于java中的可变长参数
*/
public class Test {
//m1方法有一个int类型的可变长参数
public static void m1(int... a){ //m1方法在调用的时候,传递的实参可以是0-n个
System.out.println("Test");
}
//如果有可以精确匹配的方法,则调用该方法,不会再去执行可变长参数的那个方法
public static void m1(int i){
System.out.println(i);
}
public static void main(String []args){
m1();
m1(1);
m1(1, 2);
m1(3, 4, 5, 6, 7);
/*
`输出结果为:
Test
1
Test
Test
*/
}
}


可变长参数可以使用length属性:(也可以使用下标,如args[i])
//可变长参数可以等同看作数组
public static void m2(Sring... args){
for(int i = 0; i < args.length; i++){
System.out.println(args[i]);
}
}


需要注意的是,可变长参数只能放在参数列表的最后,以下代码编译不能通过:
public static void m4(String... a, int i){}


4、IO和Properties类的联合应用:

从字节输入流中加载适当的list(键值对形式):



在指定目录下创建dbinfo.properties文件,文件内容如下:(属性文件中的格式固定,“=”号可以替换成“:”或者是空格。java中规范要求属性文件以“.properties”。如果“空格”,“等号”,“冒号”都有,按最前的作为分隔符。)



示例代码:

需要在创建的java项目中添加一个dbinfo文件:



package com.geeklicreed.j2se;

import java.io.FileInputStream;
import java.util.Properties;

/*
* 关于java中的可变长参数
*/
public class Test {

public static void main(String []args) throws Exception{
//1、创建属性对象
Properties p = new Properties(); //和Map一样,只不过key和value只能存储字符串类型
//key不能重复,如果key重复则value覆盖
//2、创建输入流
FileInputStream fis = new FileInputStream("dbinfo");

//3、将fis流中的所有数据加载到属性对象中
p.load(fis); //所以现在属性对象中有(key=username,value=scott)

//4、关闭流
fis.close();

//通过key获取value
String v1 = p.getProperty("driver");
String v2 = p.getProperty("url");
String v3 = p.getProperty("username");
String v4 = p.getProperty("password");

System.out.println(v1);
System.out.println(v2);
System.out.println(v3);
System.out.println(v4);
/*
输出结果为:
oracle.jdbc.driver.OracleDriver
jdbc:oracle:thin:@192.168.1.100:1521:geeklicreed
scott
tiger
*/
}
}


5、java.lang.Class(类)、java.lang.reflect.Field(类中的属性)和java.lang.reflect.Modifier类(类中的修饰符)

java.lang.Class类中的getFields()方法:用于获取Class类对象表示的public修饰符修饰的Field对象数组



java.lang.Class类中的getDeclaredFields()方法:用于获取Class类对象表示的所有Field对象数组:



java.lang.Class类中的getDeclaredField方法:用于获取Class类对象表示的Field对象(通过传递属性名字符串参数)



java.lang.reflect.Field类中的getType()方法:用于获取Field类对象表示的用于标识声明域的类型的Class对象



java.lang.Class类中的getName()方法:用于获取Class类对象表示的实体的名字,作为字符串返回(包括类、接口、数组类、基本数据类型或者是void)



java.lang.Class类中的getSimpleName()方法:用于获取Class类对象表示的在源码中给定的底层类简写的名字



java.lang.Class类中的getModifiers()方法:用于获取类或者接口中的java语言中的修饰符(编码成int类型)



java.lang.reflect.Field类中的getModifiers()方法:用于获取Field对象表示的域的java语言修饰符(作为int类型数据返回)



java.lang.reflect.Modifier类中的toString(int mod)方法:返回指定的修饰符标识的字符串名(通过传入iint类型的参数)



java.lang.reflect.Field类中的set(Object object, Object value)方法:通过Field类对象相关的field域设置指定的值(指定的值为第二个参数Object value)(第一个参数传递的是包含该属性的类对象)



java.lang.reflect.Field类中的get(Object obj)方法:(通过在指定的类对象上,返回这个Field对象的域的值)



java.lang.reflect.AccessibleObject类中的setAccessible(boolean flag)方法:打破封装,将这个对象的可访问标志设置为指定的布尔值(java.lang.reflect.Field继承自java.lang.reflect.AccessibleObject)



6、java.lang.reflect.Method:类中的方法

java.lang.Class类中的getDeclaredMethods()方法:用于获取Class类对象表示的所有方法对象数组,该数组反射对象类或者接口声明中的所有方法



java.lang.reflect.Method类中的getReturnType()(获取返回值的Class类型)、getModifers()(获取修饰符表示的int类型的值)、getName()(获取方法的字符串名字)、getParameterTypes()方法(获取形式参数列表表示的Class类型数组):(以下方法和java.lang.reflect.Field类中的方法类似,不赘述)









7、获取某个特定的方法,通过反射机制执行:

java.lang.Class类中的getDeclaredMethod(String name, Class ... parameterType)方法:用于获取Class类对象相关的Method类对象(通过传递字符串方法名和可变长Class类型的形参列表两个参数)



java.lang.reflect.Method类中的invoke(Object obj, Object ... args)方法:在传递的指定的对象(第一个参数)调用对象类上的方法,实参列表为传递的指定可变长参数



8、java.lang.reflect.Constructor:类的构造方法

java.lang.Class类中的getDeclaredConstructor(Class ... parameterTypes)方法:用于获取Class类对象表示的构造器对象,这个构造器对象反射指定类或者接口的构造器

java.lang.Class类中的getDeclaredConstructors()方法:用于获取Class类对象表示的构造器对象数组,这个构造器对象数组反射在类中声明的所有构造器



java.lang.reflect.Constructor类中的getModifiers()方法:用于返回Constructor类对象表示构造器的java语言修饰符(作为int类型数据返回)



java.lang.reflect.Constructor类中的getParameterTypes():用于获取Constructor类对象表示的Class类对象数组,这个类对象数组用于表示在构造器中按照一定次序形式参数类型



java.lang.reflect.Constructor类中的getGenericExceptionTypes()方法:用于返回Constructor类对象的Type类对象数组,这个数组表示声明抛出的异常



9、获取某个特定的构造方法,然后创建对象:

java.lang.reflect.Constructor类中的newInstance(Object ... initargs)方法:使用Constructor类对象表示的构造器,创建和初始化构造器声明的类的实例(使用指定的参数列表)



示例代码:

package com.geeklicreed.j2se;

import java.lang.reflect.Constructor;

public class ReflectTest {

public static void main(String[] args) throws Exception {
// 1、获取类
Class c = Class.forName("Customer");

// 2、获取特定的构造方法
Constructor con = c.getDeclaredConstructor(String.class, int.class);

// 3、创建对象
Object o = con.newInstance("张三", 25);

System.out.println(o);
}
}

class Customer {
String name;
int age;

@Override
public String toString() {
return "Customer [name=" + name + ", age=" + age + "]";
}

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

}


10、关于类获取父类和父接口:

java.lang.Class类中的getSuperclass()方法:用于获取Class类对象表示的实体(类、接口、基本数据类型或者是void)的Class父类对象



java.lang.Class类中的getInterfaces()方法:通过Class类对象表示的这个类或者接口确定它实现的接口,并返回这个Class类对象

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息