Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个 类只有一个Class对象),这个对象就包含了完整的类的结构信息。可以通过这个对象看到类的结构。
Java反射机制提供的功能:
Class实例对应着加载到内存中的一个运行时类,可以利用该实例调用和获取运行时类中的结构。一个 Class 对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。
哪些类型可以有Class对象?
(1)class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类 (2)interface:接口 (3)[]:数组 (4)enum:枚举 (5)annotation:注解@interface (6)primitive type:基本数据类型 (7)void
其他说明:
相关方法:
方法名 | 功能说明 |
---|---|
static Class forName(String name) | 返回指定类名 name 的 Class 对象 |
Object newInstance() | 调用缺省构造函数,返回该Class对象的一个实例 |
Class getSuperClass() | 返回当前Class对象的父类的Class对象 |
getName() | 返回此Class对象所表示的实体(类、接口、数组类、基本类型 或void)名称 |
Class [] getInterfaces() | 获取当前Class对象的接口 |
ClassLoader getClassLoader() | 返回该类的类加载器 |
Class getSuperclass() | 返回表示此Class所表示的实体的超类的Class |
Constructor[] getConstructors() | 返回一个包含某些Constructor对象的数组 |
Field[] getDeclaredFields() | 返回Field对象的一个数组 |
Method getMethod(String name,Class … paramTypes) | 返回一个Method对象,此对象的形参类型为paramType |
获取Class类的实例(四种方法):
读取配置文件的两种方法:
方法一,流读取:
Properties p = new Properties();
FileInputStream fi = new FileInputStream(new File("jdbc.properties"));//相对路径在module下
p.load(fi);
String username = p.getProperty("username");
String password = p.getProperty("password");
System.out.println(username+" || "+password);
方法二,类加载器读取:
Properties p1 = new Properties();
ClassLoader cl = Day11.class.getClassLoader();
InputStream is = cl.getResourceAsStream("jdbc.properties");//相对路径在module的src下
p1.load(is);
String username1 = p1.getProperty("username");
String password1 = p1.getProperty("password");
System.out.println(username1+" || "+password1);
创建类的对象:调用Class对象的newInstance()方法。要求: 1)类必须有一个无参数的构造器。 2)类的构造器的访问权限需要足够。
对于没有无参的构造器,创建对象的方式是:
//1.根据全类名获取对应的Class对象
String name = “xx.xx.Person";
Class clazz = null;
clazz = Class.forName(name);
//2.调用指定参数结构的构造器,生成Constructor的实例
Constructor con = clazz.getConstructor(String.class,Integer.class);
//3.通过Constructor的实例创建对应类的对象,并初始化类属性
Person p2 = (Person) con.newInstance("Peter",20);
System.out.println(p2);
通过反射,调用类中的方法,通过Method类完成。步骤:
Object invoke(Object obj, Object … args)说明:
在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和 get()方法就可以完成设置和取得属性内容的操作。
public class Person implements Comparable{
public int id;
private String name;
private Person(String name){
this.name = name;
}
private void showNation(String name){
System.out.println("你好,我的名字是"+name);
}
...
}
Class clazz = Person.class;
Constructor cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessible(true);
Person p2 = (Person) cons1.newInstance("刘星星");
Field name2 = clazz.getDeclaredField("name");//调用属性name
name2.setAccessible(true);
name2.set(p2,"李鑫鑫");//设置属性name
Method m2 = clazz.getDeclaredMethod("showNation",String.class);//调用方法showNation,且指定形参列表
m2.setAccessible(true);
m2.invoke(p2,"中国");//相当于p2.showNation("中国")
静态代理:
静态代理模式中代理类和被代理类在编译期间就确定下来,缺乏动态性,不利于扩展。
public class Po {
public static void main(String[] args) {
Network server = new Server();
Network client = new Client(server);
client.browse();
}
interface Network{
public void browse();
}
//被代理
class Server implements Network
@Override
public void browse() {
System.out.println("被代理者访问网络");
}
}
//代理
class Client implements Network{
private Network network;
public Client(Network network) {
this.network = network;
}
public void check(){
System.out.println("代理者在访问前的一些准备工作");
}
public void end(){
System.out.println("代理者在访问后的一些收尾工作");
}
@Override
public void browse() {
check();
network.browse();
end();
}
}
动态代理:
Proxy :专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。
其中,InvocationHandler h——得到InvocationHandler接口的实现类实例,ClassLoader loader——类加载器,
Class>[] interfaces——得到被代理类实 现的全部接口
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Human{
void getBelief();
String eat(String food);
}
class SuperMan implements Human{
@Override
public void getBelief() {
System.out.println("我是超人,我相信我可以飞!");
}
@Override
public String eat(String food) {
return "我是超人,我爱吃" + food;
}
}
class BatMan implements Human{
@Override
public void getBelief() {
System.out.println("我是蝙蝠侠,我相信我很有钱!");
}
@Override
public String eat(String food) {
return "我是蝙蝠侠,我爱吃" + food;
}
}
class ProxyFactory{
//调用此方法,返回代理类对象
public static Object getProxyInstance(Object obj){//obj是被代理对象
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), handler);
}
}
class MyInvocationHandler implements InvocationHandler{
private Object object;//需要使用被代理类对象进行赋值
public void bind(Object object){
this.object = object;
}
//当通过代理类对象调用方法A时,会自动地调用如下invoke()方法:
//将被代理类要执行的方法A的功能声明在invoke()方法中。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//method,即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法。
return method.invoke(object,args);
}
}
public class ProxyTest {
public static void main(String[] args) {
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(new SuperMan());
//当通过代理类调用方法时,会自动地调用被代理类中同名方法。
System.out.println(proxyInstance.eat("麻辣烫"));
proxyInstance.getBelief();
Human proxyInstance2 = (Human) ProxyFactory.getProxyInstance(new BatMan());
System.out.println(proxyInstance2.eat("披萨"));
proxyInstance2.getBelief();
}
}
动态代理与AOP(Aspect Orient Programming)
使用Proxy生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的意义。通常都是为指定的目标对象生成动态代理。
这种动态代理在AOP中被称为AOP代理,AOP代理可代替目标对象,AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异: AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理。
Lambda 是一个匿名函数,可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。使用它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。其本质是作为函数式接口的一个实例(只有一个抽象方法)。
Comparator<Integer> c1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
Comparator<Integer> c2 = (o1,o2)-> Integer.compare(o1,o2);//lambda表达式
Comparator<Integer> c3 = Integer::compare;//方法引用
操作符“->” ,该操作符被称为 Lambda 操作符或箭头操作符。它将 Lambda 分为两个部分:
使用:
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("我爱你!");
}
};
r1.run();
Runnable r2 = () -> System.out.println("我爱你Lambda!");
r2.run();
Consumer<String> con = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
con.accept("我爱你!");
Consumer<String> con1 = (String s) -> {
System.out.println(s);
};
con1.accept("我爱你Lambda!");
Consumer<String> con1 = (s) -> {
System.out.println(s);
};
Consumer<String> con1 = s -> {
System.out.println(s);
};
Comparator<Integer> c2 = (o1,o2)-> {
System.out.println("我爱你Lambda!");
return Integer.compare(o1,o2);
}
Comparator<Integer> c2 = (o1,o2)-> Integer.compare(o1,o2);
上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的类型推断。
总结:
只包含一个抽象方法的接口,称为函数式接口。
可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。
可以在一个接口上使用@FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用。方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,可以认为是Lambda表达式的一个语法糖。
要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致。
格式:使用操作符“::” 将类(或对象) 与方法名分隔开来。如下三种主要使用情况:
Comparator<Integer> c2 = (o1,o2)-> Integer.compare(o1,o2);//lambda表达式
Comparator<Integer> c3 = Integer::compare;//方法引用
Consumer<String> con1 = s -> System.out.println(s);//lambda表达式
Consumer<String> con1 = System.out::println;//方法引用
注意:当函数式接口方法的第一个参数是需要引用方法的调用者,并且第二个参数是需要引用方法的参数(或无参数)时:ClassName::methodName
格式: ClassName::new。与函数式接口相结合,自动与函数式接口中方法兼容。 可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象方法的参数列表一致。且方法的返回值即为构造器对应类的对象。
格式: type[] :: new
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用 Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。 也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
Stream 和 Collection 集合的区别:Collection 是一种静态的内存数据结构,而 Stream 是有关计算的。前者是主要面向内存,存储在内存中,后者主要是面向 CPU,通过 CPU 实现计算。Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。 “集合讲的是数据,Stream讲的是计算!”
注意:
使用流程:
流程注意点: