首页 > 技术文章 > java反射和泛型简介

oneflyleader 2020-07-11 16:57 原文

1. 反射

https://blog.csdn.net/codejas/article/details/78635926

1.1 概念

在运行状态中,任意一个类都能获得这个类的所有方法和属性,对于任意一个对象都能够调用它的任意属性和方法,这种运行时获得信息和动态调节对象的方法为反射。

  • getClass()

    返回当前实例的Class对象

Class类和java.lang.reflect类库一起完成反射的支持,类库中包含Field,Method,Constructor类。

public class reflectt {
	public static void main(String[] args) {
		Test test = new Test();
		Class testClass = test.getClass();
		System.out.println(testClass.getName());// 获得类的完整名(包含包信息)
		System.out.println(testClass.getSimpleName());// 获得底层类名(不含包信息)
		System.out.println(testClass.getFields());// 获得字段数组
		System.out.println(testClass.getMethods());// 获得方法数组
		System.out.println(testClass.getConstructors());// 获得构造器数组
	}
}

class Test{
	public static int count = 0;
	final String TAG = "test";
	
	public String getName01() {
		return this.TAG;
	}
	
	private String getName02() {
		return this.TAG;
	}
	
	String getName03() {
		return this.TAG;
	}
	
	protected String getName04() {
		return this.TAG;
	}
}

1.2 获得Class的方法

三种方法:

  • 对象调用getClass(),eg:object.getClass()
  • 类本身的静态字段.class,eg:myClass.class
  • 给出类的字符串就可以获得该类的Class对象,eg:Class.forName("myClass")
    • 该方法需要处理异常

1.3 通过反射获得信息

  • 构造器Construct可以创建对象,字段Field可以修改和获得对象的属性(包括私有属性),方法Method可以调用
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class reflectt {
	public static void main(String[] args) {
		try {
			Class testClass = Class.forName("Test");
			System.out.println(testClass.getName());// 获得类的完整名(包含包信息)
			System.out.println(testClass.getSimpleName());// 获得底层类名(不含包信息)
			System.out.println(testClass.getFields());// 获得字段数组
			System.out.println(testClass.getMethods());// 获得方法数组
			System.out.println(testClass.getConstructors());// 获得构造器数组
			
			// 获得和使用构造器
			Constructor<Test> constructNull = \testClass.getConstructor();// 获得无参数构造器
			Constructor<Test> constructorStr = testClass.getConstructor(String.class);// 获得参数为String的构造器
			Test objNull = constructNull.newInstance();
			Test objStr = constructorStr.newInstance("hello");// 直接使用Class可以构造对象
			
			// 获得和修改属性
			Field field = testClass.getField("t");// 只能获得public修饰的属性
			System.out.println(field.toString());//  返回类型和位置
			System.out.println(field.canAccess(new Test()));// 判断能够访问
			
			Field field2 = testClass.getDeclaredField("pri");// 获得所有声明的属性
			field2.setAccessible(true);// 使私有属性可访问
			field2.setInt(objNull, 2); // 可以破坏封装,修改私有属性
			System.out.println((int)field2.getInt(objNull));
			
			// 获得和调用方法
			Method m1 = testClass.getMethod("getName01", null);// 只能获得public方法
			System.out.println(m1.invoke(objNull, null));
			Method m2 = testClass.getMethod("getName01", String.class);
			System.out.println(m2.invoke(objNull, "hello"));
            //Method m3 = testClass.getDeclaredMethod(arg0, arg1); 获得所有方法
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
}

class Test{
	public static int count = 0;
	final String TAG = "test";
	public String t = "ss";
	private int pri = 1;
	
	public Test() {
		
	}
	
	public Test(String s) {
		
	}
	
	public String getName01() {
		return this.TAG;
	}
    public String getName01(String s) {
		return s+this.TAG;
	}
	
	private String getName02() {
		return this.TAG;
	}
	
	String getName03() {
		return this.TAG;
	}
	
	protected String getName04() {
		return this.TAG;
	}
}

android开发中的资源实例的创建就是利用反射方法的工厂模式,通过findViewById指定确定的资源id来获得要创建实例的类型和属性,并返回创建完成的实例,实现分离。

2. 静态块

静态块指的是static{}代码块,仅在类加载时有且仅执行一次,例如类实例化就执行,且无论实例化多少都仅在第一次实例化时执行一次。

import java.lang.reflect.Constructor;

public class Main {
	static {
		System.out.println("hello world");
	};
	
	public static void main(String[] args) throws Exception {
		Constructor constor = Main.class.getConstructor();
		Main m1 = (Main)constor.newInstance();
		Main m2 = (Main)constor.newInstance();
	}
}
  • 类加载的含义
    • Class.forName显式加载时
      • 也可以自己设定此时不加载类
    • 实例化
    • 调用类的静态static方法
    • 调用类的静态static变量
      • 但调用类的静态常量static final不会加载类
  • 静态块代码按照顺序先后执行
  • 先执行完static块语句才执行调用的方法
public class TestStatic{
    static{
        System.out.println(1);
    }
    static {
        System.out.println(2);
    }
    static {
        System.out.println(3);
    }
    public static void main(String args[]){
        System.out.println(5);
    }
    static {
        System.out.println(4);
    }
}
// 输出12345
————————————————
版权声明:本文为CSDN博主「lubiaopan」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lubiaopan/java/article/details/4802430

3. 泛型

面向对象类型安全的一种机制:

  • 创建泛化实例
ArrayList<Integer> a = new ArrayList<>();
  • 声明和指定泛化类型的变量
  • 函数参数泛型声明
// 常用方法是类上声明泛型(接口也有泛型)
public class ArrayList<E> extends AbstractList<E>...{
    public boolean add(E o){// 在类上定义的泛型可以在类中其他位置当作类型直接使用
        // ...
    }
}
// 不常用的方法,如果未定义在类声明的类型,需要对方法泛型
public <T extends Animal> void takeThings(ArrayList<T> a){
    // 这里的意思是T可以是任意一种Animal,即自身及其子类
    // ...
}

上面的形式不同于public void takeThings(ArrayList<Animal> a),上面形式指Animal及其子类可以作为参数,但是这里提到的方法仅支持Animal类型

  • 如果可以指定类的泛化,也就可能支持接口的泛化
public static <T extends Iterface> void quip(List<T> list){
	// ...
}

​ 如果接口也是泛化接口时:

public static <T extends Comparable<? super T>> void sort(List<T> list){
    // ...
}

推荐阅读