目录
Java的反射机制是在运行状态中,对于任意一个类,都能够知道这个类所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,我们可以修改部分的类型信息;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射(reflection)机制。
反射需要引入****java.lang.reflect
- 某个类的私有的成员变量和方法只对系统应用开放,我们可以通过反射来获取到所需要的私有的成员变量或者方法;
- 反射最重要的用途是开发各种通用的框架。比如在spring中,我们将所有的类Bean交给spring容器管理,无论是XML配置Bean还是注解配置,当我们从容器中获取Bean来依赖注入时,容器会读取数据,而配置中给的就是类的信息,spring根据这些信息,需要创建哪些Bean,spring就动态的创建这些类。
类名
用途
Class类
代表类的实体,在运行的java应用程序中表示类和接口
Filed类
代表类的成员变量/类的属性
Method类
代表类的方法
Constructor类
代表类的构造方法
常用方法:
方法
用途
getClassLoader()
获取类的加载器
getDeclaredClasses()
返回一个数组,这个数组中包含该类中所有类和接口类的对象(包含私有的)
forName(String classname)
根据类名返回类的对象
newInstance()
创建类的实例
getName()
获取类的完整路径名字
常用方法:
方法
用途
getFiled(String name)
获得某个公有的属性对象
getFileds()
获取所有的公有的属性对象
getDeclaredFiled(String name)
获取某个属性对象
getDeclaredFileds()
获取所有的属性对象
常用方法:
方法
用途
getMethod(String name, Class…<> parameterTypes)
获得该类某个公有的方法
getMethods()
获得该类所有公有的方法
getDeclaredMethod(String name, Class…<> parameterTypes)
获得该类某个方法
getDeclaredMethods()
获得该类所有方法
常用方法:
方法
用途
getConstructor(Class…<> parameterTypes)
获得该类中与参数类型匹配的公有构造方法
getConstructors()
获得该类的所有公有构造方法
getDeclaredConstructor(Class…<> parameterTypes)
获得该类中与参数类型匹配的构造方法
getDeclaredConstructors()
获得该类所有构造方法
在反射之前,第一步是先要拿到需要反射的类的class对象,然后通过class类的核心方法,达到反射的目的。
- 第一种:Class.forName(“类的全路径名”)
- 第二种:类名.class(使用.class方法)
- 第三种:使用类对象的getClass()方法
使用反射创建对象
反射构造方法
反射属性
反射方法
创建对象的几种方式:
- 使用new关键字创建
- clone()方法创建
- newInstance()→Class类
优点:
- 对于任意一个类,都能够知道这个类的所有的属性和方法;对于任意一个对象,都能够调用它的任意一个方法;
- 增加了程序的灵活性和扩展性,降低了耦合性,提高了自适应能力;
- 反射应用到了很多的框架中:Struts、Spring等等。
缺点:
- 使用反射程序的效率很低;
- 反射技术绕过了源代码的技术,因而会带来维护问题。反射的代码更加复杂。
主要用途是:将一组常量组织起来,在这之前表示一组常量通常使用定义常量的方式:
public static int final RED = 1;
public static int final GREEN = 2;
public static int final BLACK = 3;
使用常量举例存在不好的地方:如果有一个数字1,可能会误认为是RED。如果使用枚举来组织,就拥有了类型,而不是整型1。
public enum TestEnum {
RED,BLACK,GREEN;
}
本质:是Enum的子类,自己写的枚举类,即便没有显示的继承Enum,也会默认继承这个类。
枚举通常搭配switch来使用。
常用方法:
方法
描述
values()
以数组形式返回枚举类型的所有成员
ordinal()
获取枚举成员的索引位置
valueOf()
将普通字符串转化为枚举实例(已有的)
compareTo()
比较两个枚举成员在定义时的顺序
枚举类的API中没有values()方法,所以,它到底在哪里?
重要:枚举类的默认方法是私有的!!!
优点:
- 枚举常量更简单安全
- 枚举具有内置方法,代码更优雅
缺点:
- 不可继承,无法扩展
面试题:如何实现一个线程安全的单例模式?
可以通过枚举来实现一个线程安全的单例模式。(不能通过反射来获取枚举的实例)
**Lambda表达式允许通过表达式来代替接口的功能。**Lambda表达式和方法一样,提供了一个正常的参数列表和一个使用这些参数的主体(一个表达式或者一个代码块)。可以看作是一个匿名函数。
基本语法:(parameters) -> expression 或 (parameters) ->{ statements; }
Lambda表达式由三部分组成:
- parameters:类似方法的形参列表,这里的参数是函数式接口里的参数。参数类型可以声明也可以不声明由JVM隐含的推断。只有一个参数时可以省略小括号。
- →:“被用于”的意思;
- 方法体:可以是表达式也可以是代码块,是函数式接口里方法的实现。代码块等同于方法中的方法体。
函数式接口:一个接口有且只有一个抽象方法。
【注】
- 一个接口只有一个抽象方法,该接口就是一个函数式接口;
- 可以在接口上使用@FunctionalInterface注解,此时编译器会按照函数式接口的定义来要求该接口,如果接口中定义了两个抽象方法,程序编译就会报错。
定义函数式接口,可以只有一个抽象方法,也可以有其它的default方法:
//方式一:
@FunctionalInterface
interface NoParameterNoReturn {
//注意:只能有一个方法
void test();
}
//方式二:
@FunctionalInterface
interface NoParameterNoReturn {
void test();
default void test2() {
System.out.println("JDK1.8新特性,default默认方法可以有具体的实现");
}
}
以无参无返回值的函数式接口为例:
//无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn {
void test();
}
//使用匿名内部类实现接口
NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn(){
@Override
public void test() {
System.out.println("hello");
}
};
//使用Lambda表达式实现接口
NoParameterNoReturn noParameterNoReturn = ()->System.out.println("hello");
【注】
- 参数类型可以省略,如果省略,所有参数都要省略;
- 只有一个参数,参数的小括号可以省略;
- 方法体只有一条语句,大括号可以省略;
- 方法体只有一条语句,且其是return语句,大括号可以省略,且去掉return关键字。
Lambda表达式本质就是使用匿名内部类来实现接口。
对于以下代码,a就是捕获变量:
@FunctionalInterface
interface NoParameterNoReturn {
void test();
}
public static void main(String[] args) {
int a = 10;
NoParameterNoReturn noParameterNoReturn = ()->{
// a = 99; error
//变量a就是捕获变量
System.out.println("捕获变量:"+a);
};
noParameterNoReturn.test();
}
进行变量捕获时,捕获的变量要么被final修饰,如果不被final修饰,要保证变量不能被修改。
为了让Lambda表达式在Java集合中更好的使用,集合当中,新增了部分接口,以便于Lambda表达式进行对接。
接口
方法
removeIf()、stream()、forEach()、spliterator()
List
replaceAll()、sort()
Map
forEach()、replaceAll()、remove()、replace()、compute()、
merge()
//原型如下
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
//Lambda表达式
list.forEach(s -> {
System.out.println(s);
});
//按照字符串的长度进行排序
list.sort(new Comparator<String>() {
@Override
public int compare(String str1, String str2){
//注意这里比较长度
return str1.length()-str2.length();
}
});
//Lambda表达式
list.sort((str1,str2)-> str1.length()-str2.length());
default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
map.forEach(new BiConsumer<Integer, String>(){
@Override
public void accept(Integer k, String v){
System.out.println(k + "=" + v);
}
});
//Lambda表达式
map.forEach((k,v)-> System.out.println(k + "=" + v));
优点:
- 代码简洁,开发迅速
- 方便函数式编程
- 非常容易进行并行计算
- 改善了集合操作
缺点:
- 代码可读性变差
- 不容易进行调试
- 在非并行计算中,很多计算没有for的性能高