Java基础 - 泛型与反射

本文最后更新于:4 天前

泛型

概念

Java 泛型(generics) 是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

泛型擦除是指 Java 在运行期间,所有的泛型信息都会被擦掉。

常用的通配符为: T,E,K,V,?

  • ? 表示不确定的 Java 类型
  • T (type) 表示具体的一个 Java 类型
  • K V (key value) 分别代表 Java 键值中的 Key Value
  • E (element) 代表 Element

泛型类

1
2
3
4
5
6
7
8
9
10
11
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T> {
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey() {
return key;
}
}

实例化

1
Generic<Integer> genericInteger = new Generic<Integer>(123456);

除此之外,泛型个数可以自由指定,被称为多元泛型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Notepad<K,V>{       // 此处指定了两个泛型类型  
private K key ; // 此变量的类型由外部决定
private V value ; // 此变量的类型由外部决定
public K getKey(){
return this.key ;
}
public V getValue(){
return this.value ;
}
public void setKey(K key){
this.key = key ;
}
public void setValue(V value){
this.value = value ;
}
}

public class GenericsDemo09{
public static void main(String args[]){
Notepad<String,Integer> t = null ; // 定义两个泛型类型的对象
t = new Notepad<String,Integer>() ; // 里面的key为String,value为Integer
t.setKey("汤姆") ; // 设置第一个内容
t.setValue(20) ; // 设置第二个内容
System.out.print("姓名;" + t.getKey()) ; // 取得信息
System.out.print(",年龄;" + t.getValue()) ; // 取得信息

}
}

泛型接口

1
2
3
public interface Generator<T> {
public T method();
}

实现接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//不指定类型实现泛型接口
class GeneratorImpl<T> implements Generator<T>{
@Override
public T method() {
return null;
}
}

//指定类型实现
class GeneratorImpl implements Generator<String>{
@Override
public String method() {
return "hello";
}
}

泛型方法

1
2
3
4
5
6
public static <E> void printArray(E[] inputArray) {
for (E element : inputArray) {
System.out.printf("%s ", element);
}
System.out.println();
}

调用泛型方法:

1
2
3
4
5
// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3 };
String[] stringArray = { "Hello", "World" };
printArray(intArray);
printArray(stringArray);

除此之外,可以通过class类来定义泛型方法:

1
2
3
4
5
6
7
8
9
public <T> T getObject(Class<T> c) throws InstantiationException, IllegalAccessException{
T t = c.newInstance();
return t;
}

//调用泛型方法
Generic generic = new Generic();
//通过泛型返回objectName类的实例obj
Object obj = generic.getObject(Class.forName("com.XCC.objectName"));

反射

概念

反射就是把java类中的各种成分映射成一个个的Java对象。

==对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性==

这里我们首先需要理解 Class类,以及类的加载机制; 然后基于此我们如何通过反射获取Class类以及类中的成员变量、方法、构造方法等。

类加载过程

应用场景

动态代理和注解都依赖于反射机制实现。

获取 Class 对象的四种方式

在类加载的时候,jvm会创建一个class对象,如果我们动态获取到这些信息,我们需要依靠 Class 对象。Class 类对象将一个类的方法、变量等信息告诉运行的程序。

  1. 具体类

    1
    Class<?> alunbarClass = TargetObject.class; //这里TargetObject代表的是该类的对象
  2. 通过类的全路径(全限定类名)

    1
    Class<?> alunbarClass1 = Class.forName("cn.javaguide.TargetObject");
  3. 通过对象实例

    1
    2
    TargetObject o = new TargetObject();
    Class<?> alunbarClass2 = o.getClass();
  4. 通过类加载器

    通过类加载器获取 Class 对象不会进行初始化,意味着不进行包括初始化等一系列步骤,静态代码块和静态对象不会得到执行。

    1
    Class<?> clazz = ClassLoader.loadClass("cn.javaguide.TargetObject");

获取某个类的方法以及参数

Method 获取方法

适用Method反射类用来实现动态代理,适用invoke()函数能调用相应的方法。

Method类的invoke(Object obj,Object... args)第一个参数代表调用的对象,第二个参数传递的调用方法的参数。

值得注意的是getDeclaredMethods()返回的是此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。getMethods()返回的是所有包括继承的公共member方法。

方法名称 方法说明
getDeclaredMethod(String name, Class<?>… parameterTypes) 返回一个指定参数的Method对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法
getDeclaredMethods() 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法
getMethod(String name, Class<?>… parameterTypes) 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法
getMethods() 返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法
Field 获取参数

Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。

方法名称 方法说明
getDeclaredField(String name) 获取指定name名称的(包含private修饰的)字段,不包括继承的字段
getDeclaredFields() 获取Class对象所表示的类或接口的所有(包含private修饰的)字段,不包括继承的字段
getField(String name) 获取指定name名称、具有public修饰的字段,包含继承字段(如果需要获取父类的public字段)
getFields() 获取修饰符为public的字段,包含继承字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package javaBase;


import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class InvokeDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {
/**
* 获取 TargetObject 类的 Class 对象并且创建 TargetObject 类实例
*/
//cn.javaguide.TargetObject 是相应类的全限定类名
Class<?> targetClass = Class.forName("javaBase.TargetObject");
//实例化默认构造方法,TargetObject必须无参构造函数,否则将抛异常
TargetObject targetObject = (TargetObject) targetClass.newInstance();

//获取公有的构造函数且不带参数
Constructor<?> constructor = targetClass.getConstructor();
System.out.println(constructor.getName());
//获取私有的构造函数(且带参数)
Constructor<?> constructorString = targetClass.getDeclaredConstructor(String.class);
//通过Constructor的newInstance方法新建一个实例
TargetObject targetObjectByCon = (TargetObject) constructorString.newInstance("newTargetObject");
targetObjectByCon.getValue();
//获取所有构造参数类型
Constructor<?>[] constructors = targetClass.getDeclaredConstructors();
int count = 0;
for (Constructor<?> constructorTemp : constructors){
//获取构造参数类型
Class<?>[] clazz = constructorTemp.getParameterTypes();
System.out.println("constructors"+count+": ");
for (Class<?> classTemp:clazz){
System.out.println(classTemp.getName());
}
count++;
}


/**
* 获取 TargetObject 类中定义的所有方法
*/
System.out.println("#########################");
Method[] methods = targetClass.getDeclaredMethods();
for (Method method : methods) {
System.out.println("Method: "+method.getName());
}

/**
* 获取指定方法并调用
*/
Method publicMethod = targetClass.getDeclaredMethod("publicMethod",
String.class);
publicMethod.invoke(targetObject, "World");

/**
* 获取指定方法并调用并输出返回值
*/
Method publicMethodInt = targetClass.getDeclaredMethod("publicMethodInt",int.class);
System.out.println(publicMethodInt.invoke(targetObject,520));

/**
* 获取指定参数并对参数进行修改
*/
System.out.println("#########################");
Field field = targetClass.getDeclaredField("value");
Field[] fields = targetClass.getDeclaredFields();
for (Field fieldItem:fields){
System.out.println("Field: "+fieldItem.getName());
}
//为了对类中的参数进行修改我们取消安全检查 如果不取消安全检查再用filed去get或set的private的字段
field.setAccessible(true);
System.out.println("value: "+field.get(targetObject));
field.set(targetObject, "TargetObject2");
System.out.println("value: "+field.get(targetObject));


/**
* 调用 private 方法
*/
System.out.println("#########################");
Method privateMethod = targetClass.getDeclaredMethod("privateMethod");
//为了调用private方法我们取消安全检查
privateMethod.setAccessible(true);
privateMethod.invoke(targetObject);
}
}

上方对应的TargetObject类定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package javaBase;

public class TargetObject {
public static String staticString = "StaticString";

private String value;

public TargetObject() {
value = "TargetObject";
}

public TargetObject(String s) {
value = s;
}

public int publicMethodInt(int n){
return n;
}

public void getValue(){System.out.println(value);}

public void publicMethod(String s) {
System.out.println("Hello " + s);
}

private void privateMethod() {
System.out.println("value is " + value);
}
}

输出结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
javaBase.TargetObject
newTargetObject
constructors0:
constructors1:
java.lang.String
#########################
Method: getValue
Method: publicMethod
Method: publicMethodInt
Method: privateMethod
Hello World
520
#########################
Field: staticString
Field: value
value: TargetObject
value: TargetObject2
#########################
value is TargetObject2

Constructor 构造实例

Constructor类存在于反射包(java.lang.reflect)中,反映的是Class 对象所表示的类的构造方法

方法返回值 方法名称 方法说明
static Class<?> forName(String className) 返回与带有给定字符串名的类或接口相关联的 Class 对象。
Constructor getConstructor(Class<?>… parameterTypes) 返回指定参数类型、具有public访问权限的构造函数对象
Constructor<?>[] getConstructors() 返回所有具有public访问权限的构造函数的Constructor对象数组
Constructor getDeclaredConstructor(Class<?>… parameterTypes) 返回指定参数类型、所有声明的(包括private)构造函数对象
Constructor<?>[] getDeclaredConstructors() 返回所有声明的(包括private)构造函数对象
T newInstance() 调用无参构造器创建此 Class 对象所表示的类的一个新实例

主类 ConstructionTest:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class ConstructionTest implements Serializable {
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("javaBase.User");
//获取带String参数的public构造函数
Constructor cs1 = clazz.getConstructor(String.class);
//创建User
User user1= (User) cs1.newInstance("hiway");
user1.setAge(22);
System.out.println("user1:"+user1.toString());

System.out.println("#########################");

//取得指定带int和String参数构造函数,该方法是私有构造private
Constructor cs2=clazz.getDeclaredConstructor(int.class,String.class);
//由于是private必须设置可访问
cs2.setAccessible(true);
//创建user对象
User user2= (User) cs2.newInstance(25,"hiway2");
System.out.println("user2:"+user2.toString());

System.out.println("#########################");

//获取所有构造包含private
Constructor<?> cons[] = clazz.getDeclaredConstructors();
// 查看每个构造方法需要的参数
for (int i = 0; i < cons.length; i++) {
//获取构造函数参数类型
Class<?> clazzs[] = cons[i].getParameterTypes();
System.out.println("构造函数["+i+"]:"+cons[i].toString() );
System.out.print("参数类型["+i+"]:(");
for (int j = 0; j < clazzs.length; j++) {
if (j == clazzs.length - 1)
System.out.print(clazzs[j].getName());
else
System.out.print(clazzs[j].getName() + ",");
}
System.out.println(")");
}
}
}

反射的对象 User:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
class User {
private int age;
private String name;
public User() {
super();
}
//公有构造
public User(String name) {
super();
this.name = name;
}

/**
* 私有构造
* @param age
* @param name
*/
private User(int age, String name) {
super();
this.age = age;
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}

输出结果如下:

1
2
3
4
5
6
7
8
9
10
user1:User{age=22, name='hiway'}
#########################
user2:User{age=25, name='hiway2'}
#########################
构造函数[0]:private javaBase.User(int,java.lang.String)
参数类型[0]:(int,java.lang.String)
构造函数[1]:public javaBase.User(java.lang.String)
参数类型[1]:(java.lang.String)
构造函数[2]:public javaBase.User()
参数类型[2]:()