前言
再介绍反射之前,要了解什么是动态语言什么是静态语言
动态语言
动态语言是一类在运行时可以改变其数据结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。
通俗点说就是在运行时代码可以根据某些条件改变自身结构
举个例子
function test() {
var x = "var a=3;var b=5;alert(a+b)";
eval(x);
}
这是一个叫做test的函数,函数里面定义了一个变量x,它是一个字符串,eval()是接受一个字符串并执行,所以这个函数就是定义了一个字符串然后执行
在函数初始化的时候,x的数据类型是字符串,然后在运行时,eval()方法把x的数据类型由字符串类型改成了整型,也就是说前后x的数据类型改变了,这是动态语言的特点,所以说javascript是一门动态语言
主要的动态语言:object-C、C#、JavaScript、PHP、Python
静态语言
与动态语言相对应的,运行时数据结构不可变的语言就是静态语言。如Java、C、C++
Java不是动态语言,但Java可以称之为“准动态语言”。
Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。
Java的动态性让编程的时候更加灵活。
反射
通常在创建对象的时候,在类加载区就会识别对象的数据类型
而反射是在运行期动态地获取对象的数据类型
Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在运行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性以及方法
正常方式: 引入需要对象的“包类”名称————通过new实例化————取得实例化对象
反射方式: 实例化对象————getClass()方法————得到完整的“包类”名称
Class clazz = person.getClass();
JVM类的加载流程和内存结构
class文件包含的内容
反射关键类图
生成对象的步骤对比
通过反射创建对象的3种方式
public class Person {
public String name = "com/reflect";
protected Integer age = 1;
private Byte sex = (byte) 1;
Boolean isMarriage = true;
// 无参数
public Person() {
}
// 有参数
public Person(String name, Integer age, Byte sex, Boolean isMarriage) {
this.name = name;
this.age = age;
this.sex = sex;
this.isMarriage = isMarriage;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Byte getSex() {
return sex;
}
public void setSex(Byte sex) {
this.sex = sex;
}
public Boolean getMarriage() {
return isMarriage;
}
public void setMarriage(Boolean marriage) {
isMarriage = marriage;
}
private String privateMethod() {
return "This is private method!";
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", isMarriage=" + isMarriage +
'}';
}
}
public class Person1 {
public String name;
protected Integer age;
private Byte sex;
Boolean isMarriage1;
public Person1() {
}
public Person1(String name, Integer age, Byte sex, Boolean isMarriage1) {
this.name = name;
this.age = age;
this.sex = sex;
this.isMarriage1 = isMarriage1;
}
public Boolean getMarriage1() {
return isMarriage1;
}
public void setMarriage1(Boolean marriage1) {
isMarriage1 = marriage1;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Byte getSex() {
return sex;
}
public void setSex(Byte sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Person1{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", isMarriage1=" + isMarriage1 +
'}';
}
}
单例类
public class SingletonPerson {
private static final SingletonPerson instance = new SingletonPerson();
private SingletonPerson(){
}
public static SingletonPerson getInstance(){
return instance;
}
}
测试类
public void test() throws Throwable{
// 方式一 类.class
Class personClazz = Person.class;
// 方式二 实例.getClass()
Person person = new Person();
Class personClazz1 = person.getClass();
// 方式三 Class.forName("类的全路径")
Class personClazz2 = Class.forName("com.reflect.Person");
System.out.println(personClazz == personClazz1);
System.out.println(personClazz == personClazz2);
}
通过Class创建实例对象
// 无参数
<bean id="person" class="com.reflect.Person" />
// 有参数
<bean id="person" class="com.reflect.Person" >
<constructor-arg index="0" type="java.lang.String" value="yang"/>
</bean>
public void test2() throws Throwable{
/** 首先:获得Person的字节码 */
Class personClazz = Class.forName("com.reflect.Person");
/** 其次:通过Class对象,创建构造方法对象 */
Constructor constructor1 = personClazz.getConstructor(); // 初始化无参构造方法
Constructor constructor2 = personClazz.getConstructor(String.class, Integer.class, Byte.class, Boolean.class); // 初始化有参构造方法对象
/** 最后:通过构造方法创建对象 */
// 调用无参数构造方法创建Person对象
Person person1 = (Person) constructor1.newInstance();
person1.setName("yang1");
System.out.println("person1=" + person1);
// 调用有参数构造方法创建Person对象
Person person2 = (Person) constructor2.newInstance("yang2", 10, (byte) 1, true);
System.out.println("person2=" + person2);
/** 补充内容:反射通过私有构造方法创建对象,破坏单例模式 */
Class singletonPersonClazz = SingletonPerson.class;
// Constructor constructor3 = singletonPersonClazz.getConstructor();
Constructor constructor3 = singletonPersonClazz.getDeclaredConstructor();
constructor3.setAccessible(true);
SingletonPerson singletonPerson = (SingletonPerson) constructor3.newInstance();
SingletonPerson singletonPerson1 = SingletonPerson.getInstance();
SingletonPerson singletonPerson2 = SingletonPerson.getInstance();
}
通过反射获取public属性的Field
public void test3() throws Throwable{
// 第一步:获得Class
Class personClazz = Person.class;
// 第二步:获得构造方法
Constructor<Person> constructor = personClazz.getConstructor();
Person person = constructor.newInstance(); // 初始化生成Person对象
// 第三步:通过Class对象,获得Field对象 Person[类]的name属性。
Field nameField = personClazz.getField("name");
// 第四步:操作Field,获得属性值
String name = String.valueOf(nameField.get(person));
System.out.println(name);
}
通过反射获取private属性的Field
public void test4() throws Throwable{
// 第一步:获得Class
Class personClazz = Person.class;
// 第二步:获得构造方法
Constructor<Person> constructor = personClazz.getConstructor();
Person person = constructor.newInstance();
// 第三步:通过Class对象,获得Field对象
Field sexField = personClazz.getDeclaredField("sex");
//setField为true时才能访问到private属性
sexField.setAccessible(true);
// 第四步:操作Field,获得属性值
System.out.println(sexField.get(person));
}
通过反射获取protect属性
public void getProtectedField() throws Throwable {
/** 首先:获得Person的字节码 */
Class personClazz = Person.class;
/** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
Person person = (Person) personClazz.getConstructor().newInstance();
/** 第三:通过Class对象,获得Field对象 */
// Field ageField = personClazz.getField("age"); // java.lang.NoSuchFieldException: age
Field ageField = personClazz.getDeclaredField("age");
ageField.setAccessible(true); // 必须设置为true,如果不设置,则报错:java.lang.IllegalAccessException: Class ReflectionTest
// can not access a member of class com.muse.Person with modifiers "protected"
/** 最后:获取字段的类型 */
Integer age = (Integer) ageField.get(person);
System.out.println(age);
}
通过反射获得default属性
public void getDefaultField() throws Throwable {
/** 首先:获得Person的字节码 */
Class personClazz = Person.class;
/** 其次:获得Person对象(由于非静态非private的属性,访问使用 对象.属性方式访问,所以反射必须先获得对象实例)*/
Person person = (Person) personClazz.getConstructor().newInstance();
/** 第三:通过Class对象,获得Field对象 */
// Field isMarriageField = personClazz.getField("isMarriage"); // java.lang.NoSuchFieldException: isMarriage
Field isMarriageField = personClazz.getDeclaredField("isMarriage");
isMarriageField.setAccessible(true); // 必须设置为true,如果不设置,则报错:java.lang.IllegalAccessException: Class ReflectionTest can not access a member of class com.muse.Person with modifiers ""
/** 最后:获取字段的类型 */
Boolean isMarriage = (Boolean) isMarriageField.get(person);
System.out.println(isMarriage);
}
}
采用反射机制来实现一个工具BeanUtils, 可以将一个对象属性相同的值赋值给另一个对象
public class BeanUtils {
public static void convertor(Object originObj, Object targetObj) throws Throwable {
// 第一步,获得class对象
Class orginClazz = originObj.getClass();
Class targetClazz = targetObj.getClass();
// 第二步,获得Field
Field[] orginFields = orginClazz.getDeclaredFields();
Field[] targetFields = targetClazz.getDeclaredFields();
// 第三步:赋值
for (Field originField : orginFields) {
for (Field targetField : targetFields) {
if (originField.getName().equals(targetField.getName())) {
originField.setAccessible(true);
targetField.setAccessible(true);
targetField.set(targetObj, originField.get(originObj));
}
}
}
}
public static void main(String[] args) throws Throwable{
// Service层返回的
Person person = new Person("com/muse", 10, (byte)1, true);
// 需要返回实体对象
Person1 person1 = new Person1();
BeanUtils.convertor(person, person1);
System.out.println("person" + person);
System.out.println("person1" + person1);
}
}
泛型
泛型的本质是指数据类型参数化
允许在定义类、接口、方法时使用类型形参,当使用时指定具体类型
所有使用该泛型参数的地方都被统一化,保证类型一致。如果未指定具体类型,默认时object类型
集合体系中所有类都增加了泛型,泛型也主要用在集合
泛型类
public class ClassGenericity {
public static void main(String[] args) {
/** 创建ObjectTool对象并指定元素类型为String */
ObjectTool<String> stringTool = new ObjectTool<>();
stringTool.setObj("yang");
System.out.println(stringTool.getObj());
/** 创建ObjectTool对象并指定元素类型为Integer */
ObjectTool<Integer> integerTool = new ObjectTool<>();
// integerTool.setObj("muse"); // 编译报错
integerTool.setObj(10);
System.out.println(integerTool.getObj());
}
/**
* 构建可以存储任何类型对象的工具类
*/
static class ObjectTool<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
}
泛型方法
public class MethodGenericity<T> {
public static void main(String[] args) {
//创建对象
ObjectTool tool = new ObjectTool();
/** 调用方法,传入的参数是什么类型,T就是什么类型 */
tool.show("hello");
tool.show(12);
tool.show(12.5f);
}
static class ObjectTool {
//定义泛型方法
public <T> void show(T t) {
System.out.println(t);
}
}
}
泛型测试
public void test(List<Object> list) { // List<Object> 和 Object
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + " ");
}
}
泛型定义的是List< Object>类型,那么传递的参数数据类型也要是OBJECT_LIST类型
通过通配符限制泛型的类型
泛型的上限:只能接收Number或者Number的子类
格式:类型名称<? extends 类>对象名
public void test(List<? extends Number> list) {
// list.add(list.get(0)); // 编译错误
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + " ");
}
}
泛型的下限:只能接收该类型及其父类型
public void test(List<? super Number> list) {
// list.add(list.get(0)); // 编译错误
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + " ");
}
}
泛型的擦除与桥接方法
泛型是提供给Javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛型的java程序后,生成的class文件中将不再带有泛型信息,以此使程序运行效率不受到影响,这个过程称之为==“擦除”==
由于类型被擦除了,为了维持多态性,所以编译器就自动生成了桥接方法
泛型接口
public interface Animal<T> {
void eat(T t);
}
泛型擦除
public class Cat implements Animal<String> {
@Override
public void eat(String s) {
System.out.println("cat eat " + s);
}
}
桥接方法
public class BridgeMethodTest {
public static void main(String[] args) {
Animal animal = new Cat();
animal.eat(new Object());
}
}