17Java进阶——反射、进程、Java11新特性教程
1.Java反射机制
Java反射(Reflection)概念:在运行时动态获取类的信息以及动态调用对象方法的功能。
1.1反射的应用——通过全类名获取类对象及其方法
package two.reflection;
import java.util.Scanner;
import java.lang.reflect.Method;
public class Test1 {
}
class TestRef {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.print("请输入一个Java类全名:");
String cName = input.next();
showMethods(cName);
}
public static void showMethods(String name) {
try {
//使用Class.forName()获得Class对象
//Class类存在于java.lang.Class
Class c = Class.forName(name);
//获得该类声明的方法,返回一个Method集合
//Method类位于java.lang.reflect包下
Method m[] = c.getDeclaredMethods();
System.out.print("该Java类的方法有:");
for (int i = 0; i < m.length; i++) {
//将方法名、修饰符、参数列表输出
System.out.println(m[i].toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
请输入一个Java类全名:two.reflection.TestRef
该Java类的方法有:public static void two.reflection.TestRef.main(java.lang.String[])
public static void two.reflection.TestRef.showMethods(java.lang.String)
eclipse等开发环境中,使用对象会显示其方法和属性,就利用了java的反射机制。
1.2 java.lang
包中的 Class
类和 java.lang.reflect
包中的 Method
类、Field
类、Constructor
类、Array
类
常用方法:类名:
Class.forName(全类名):获得类对象
类名.class:获取Class对象
包装类.TYPE:获得Class对象
对象.getClass():获取Class对象
class.getSuperClass():通过class对象获取父类对象
class.getName():获取类的全类名
字段:
class.getFields():获取所有public属性(含继承来的属性)
class.getDeclaredFields():获取所有访问权限的属性(不含继承来的属性)
方法:
class.getMethods():获取所有public修饰的方法,除构造函数(含继承来的方法)
class.getDeclaredMethods():获取所有访问权限的方法,除构造函数(不含继承来的方法)
class.getMethods(String name,Class[] args):获取特定的方法
class.getDeclaredMethods(String name,Class[] args):获取特定的方法
构造方法:(返回类型是Constructor)
class.getConstructors():获取公共的构造函数
class.getConstructors(Class[] args):获取指定参数列表的构造函数
接口:
class.getInterfaces():获取该类或者接口实现的接口数组
class.newInstance():使用无参构造方法创建该类的一个新实例
方法名、参数列表、返回值:
m.getName():获取方法名
m.getDeclaringClass():获取调用方法的类/接口
m.getParameterTypes():获取形参列表的Class数组
m.getReturnType():获取返回值类型
m.getModifiers():获取修饰符
1.3 利用反射构造对象
获得一个类的Class对象,使用class.newInstance()的方法得到Object对象,再强制转型获得实例对象。
就可以调用对象的公开方法,获得对象的公开属性。
如果类中没有默认的无参构造方法,会报InstantiationException异常。
1.4利用反射获得的构造方法实例化
无参构造:getDeclaredConstructor()获得无参构造Constructor对象,调用newInstance()方法。得到Object对象后强制类型转换。
带参构造:getDeclaredConstructor((Class[] args))获得带参构造Constructor对象,调用newInstance(Object[] o)方法,输入传入的参数。得到Object对象后强制类型转换。
1.5使用反射修改属性访问权限
Field f = c.getDeclaredField(name);
f.setAccessible(true); //取消属性的访问权限控制,即使private属性也可以进行访问
f.get(sup));获取字段值
f.set(sup,20));设置字段值
1.6使用反射调用特定方法
Method m = c.getDeclaredMethod(name,params);
m.invoke(Object o,Object[] args):调用对象o对应方法,对象o是调用方法的对象
1.7使用反射包下Array类构造动态数组
Array.newInstance(Class componentType, int length):返回一个Object类型的数组
Array.getXxx(Object array, int index):返回下标元素,为xxx类型
Array.setXxx(Object array, int index,xxx val):将下标为index的元素修改为xxx类型的值
int dim[] = {8, 10};
Object arr = Array.newInstance(Integer.TYPE, dim);:创建一个8*10的二维数组
没有赋值的情况下,和数组没有赋值的情况是相同的。
2 线程补充知识
2.1线程的三大优势
系统开销小
方便通信和资源共享
简化程序结构
2.2线程的其他常用方法
void interrupt():中断线程的阻塞状态(而非中断线程)
boolean isAlive():判定该线程是否处于活动状态,处于就绪、运行和阻塞状态的都属于活动状态。
boolean isDaemon():判断一个线程是否是守护线程
3 注解
注解是 Java 代码中的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。
注解以@开头,注解有不带参数的,一个参数的和多个参数的。
内建注解:@Override @Deprecated @SuppressWarnings
@Override:重写方法
@Deprecated:已过时
@SuppressWarnings:抑制警告。可以将value设置为以下值
deprecation:使用了过时的程序元素。 unchecked:执行了未检查的转换。 unused:有程序元素未被使用。 fallthrough:switch 程序块直接通往下一种情况而没有 break。 path:在类路径中有不存在的路径。 serial:在可序列化的类上缺少 serialVersionUID 定义。 finally:任何 finally 子句都不能正常完成。 all:所有情况。
元注解:@Target @Retention @Documented @Inherited
@Target:指定被修饰的注解能修饰哪些元素。value可以设置为以下值
ElementType.ANNOTATION\_TYPE:注解类型声明 ElementType.CONSTRUCTOR:构造方法声明 ElementType.FIELD:字段声明(包括枚举常量) ElementType.LOCAL\_VARIABLE:局部变量声明 ElementType.METHOD:方法声明 ElementType.PACKAGE:包声明 ElementType.PARAMETER:参数声明 ElementType.TYPE:类、接口(包括注解类型)或枚举声明
@Retention:指定被修饰的注解可以保留多长时间
RetentionPolicy.CLASS:编译器将把注解记录在 class 文件中,当运行 Java 程序时,虚拟机不再保留注解 RetentionPolicy.RUNTIME:编译器将把注解记录在 class 文件中,当运行 Java 程序时,虚拟机保留注解,程序可以通过反射获取该注解 RetentionPolicy.SOURCE:编译器将直接丢弃被修饰的注解
@Documented:如果添加该注解,那么所有被该注解修饰的注解出现在使用的类的javadoc中。
@Inherited:修饰的注解是可以被继承的
3.1 自定义注解并完成赋值
4 Java11新特性
4.1Lambda表达式
lambda表达式用法:简化函数式接口的实现。
lambda表达式格式:()->语句
()是形参列表,没有参数为(),一个参数为(s),两个参数为(s1,s2)。可以省略形参的类型,也可以全部写上。形参列表的变量需要提前定义好。
语句可以是一条,不需要写大括号,可以有返回值也可以没有。如果写上return ,必须要有大括号。如果是多条,需要写大括号。
lambda表达式可以作为参数传入。
A b = (z,x)->z+x; A ccc = (int z,int x)->z+x;//都是正确实现
interface A{ int add(int a,int b); }
函数式接口:接口中只有一个抽象方法。可以用@FunctionalInterface修饰,也可以不修饰
四大函数式接口:
Consumer
Predicate
Function<T,R>{R apply(T t);}
Supplier
Comparable的lambda表达式:Arrays.sort(a,((o1, o2) -> o2-o1));
4.2 方法引用
引用某个对象的实例方法:对象名 :: 非静态方法
引用类中的实例方法:类名 :: 非静态方法
引用构造方法:类名:: new
引用数组:元素类型[] :: new
4.2.接口的默认方法
jdk11后允许方法中有默认方法。修饰符是public default,default不能省略。
实现类可以重写接口中的抽象方法,但是同时实现多个接口且默认方法重名,重写方法会引起编译错误。
解决方法:实现类重写重名方法,内部使用接口名.super.方法名(),让重名方法转化为一个接口的实现。