-
17Java进阶——反射、进程、Java11新特性
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的二维数组
没有赋值的情况下,和数组没有赋值的情况是相同的。
1.8类加载
当某一个程序在运行时,需要使用某一个类,如果该类还未被加载到内存中,则系统会通过类的加载,类的链接,类的初始化三个步骤来完成对类的初始化。
类的加载:
就是指将class文件读入内存,并位置创建一个java.lang.Class对象。
任何类被使用时,系统都会为之建立一个java.lang.Class对象。
类的链接:
验证阶段:用户检验被加载的类是否有完整的结构,并和其他的类协调一致。
准备阶段: 负责为类的变量分配内存,并设置默认初始值
解析阶段: 将类的二进制数据中的符号引用替换为直接引用
类的初始化:
主要就是对类的变量进行初始化
类的初始化步骤:
假如类还未被加载和链接,则程序先加载并链接该类
假如该类的直接父类还未被加载,则先初始化其直接父类
假如类中具有初始化语句,则系统将一次执行这些初始化语句
注意:在自行第二个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1--3
类的初始化时机:
创建类的实例;
调用类的类方法
访问类或者接口的类变量或者为该类变量赋值
使用反射方式来强制创建某个类或者接口对应的Class对象
初始化某个类的子类
直接使用java
命令来运行某个主类
1.9类加载器
作用:
负责将class文件加载到内存中,并为之生成对应的Class,虽然我们不用过分的关心类的加载机制,但是了解这个机制就能更好的理解程序的运行
jvm的类加载机制:
全盘负责: 就是当一个类加载器负责加载某个Class的时候,该Class所以来的和引用的其他的Class也将有该类加载器负责载入,除非显式的使用另外一个类加载器来载入
父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载,只有在父类加载器无法加载该类时,才尝试从自己的类路径中加载该类
缓存机制:保证所有的加载过得Class在再次使用时,都并不会被重新加载,所有加载过得Class都会被缓存。当需要某个Class对象的时候,类加载器先从缓存区中搜索,只有当缓存区不存在时,系统采用读取该类对应的二进制数据,并将其转换为Class对象,存储到缓存区。
1.10java中的内置类加载器
Bootstrap class Load: 这是jvm的内置类加载器 通常表示为null,并且没有父类
Paltform class load: 平台类加载器 可以看到所有的平台,包括平台类加载器或其祖先定义的JAVASE平台的API
System class load 应用程序类加载器 与平台类加载器不同,系统类加载器加载通常用于定义应用程序类路径和模块路径和jdk特定工具类
类加载器的继承关系:
System的父加载器为Platform 而platform的父加载器为Bootstrap
public static void main(String[] args) { ClassLoader loader = ClassLoader.getSystemClassLoader(); System.out.println(loader);//AppClassLoader ClassLoader loader1 = loader.getParent(); System.out.println(loader1);//PlatformClassLoader System.out.println(loader1.getParent());// null BootstrapClassLoader }
1.11反射对于泛型的检查
@Test public void reflectTest01() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { List<Integer> list = new ArrayList<>(); list.add(10); list.add(20); // 利用反射 在运行期给list添加元素 发现泛型检查已经失效 泛型擦除 Class<? extends List> c = list.getClass(); Method addMethod = c.getMethod("add",Object.class); addMethod.invoke(list,"aaaa"); addMethod.invoke(list,true); System.out.println(list); }
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<T>{void consume(T t);}
Predicate<T>{boolean test(T t);}
Function<T,R>{R apply(T t);}
Supplier<T> {T get();}
Comparable的lambda表达式:Arrays.sort(a,((o1, o2) -> o2-o1));
4.2 方法引用
引用某个对象的实例方法:对象名 :: 非静态方法
引用类中的实例方法:类名 :: 非静态方法
引用构造方法:类名:: new
引用数组:元素类型[] :: new
4.2.接口的默认方法
jdk11后允许方法中有默认方法。修饰符是public default,default不能省略。
实现类可以重写接口中的抽象方法,但是同时实现多个接口且默认方法重名,重写方法会引起编译错误。
解决方法:实现类重写重名方法,内部使用接口名.super.方法名(),让重名方法转化为一个接口的实现。
4.3重复注解
允许在同一声明类型(类,属性,或方法)上多次使用同一个注解。Java8以前的版本使用注解有一个限制是相同的注解在同一位置只能使用一次,不能使用多次。Java 8 引入了重复注解机制,这样相同的注解可以在同一地方使用多次。
需要重复的注解,要添加@Repeatable注解修饰。
4.4Stream流
Stream是java.util.stream包下的类。主要是方便集合操作,定义了一系列的方法。
生成流:collection<T>.stream()方法获得一个Stream<T>对象。
Stream.of(T t)/Stream.of(T... values):返回一个或多个元素的Stream对象
Arrays.stream(T[] array):通过数组构造stream
中间操作:
fiilter(Predicate<? super T> p):传入一个Predicate判断函数式接口,过滤掉不符合的值。
limit(long n):只获取前n个值
skip(long n):跳过前n个值
sorted(Comparator<? extends T> com):传入外部比较器,返回排序结果
sorted():返回自然排序的结果
peek(Consumer<? super T> action):被设计用来Debug
map(Function<? super T,? extends R> mapper):传入一个Function转换函数式接口,将值转换成另一个类型
mapToInt(ToIntFunction<? super T> mapper):返回一个IntStream
mapToLong(ToLongFunction<? super T> mapper):返回一个LongStream
Stream.concat(Stream s1,Stream s2):将第二个流连接到第一个流上
distinct():唯一的值组成的流
终止操作:
count():返回元素个数
forEach(Consumer<? super T>action):对每个元素执行操作
注意,中间操作peek并不能输出每个元素的值,只有终结操作forEach才可以输出每个元素的值。因为中间操作返回流,只有终止操作执行时,stream才会真正开始执行。
4.5方法引用
方法引用的定义:更紧凑、更便捷的lambda表达式
::是引用运算符,所在的表达式称为方法引用。
构造引用可以是类名::new,类型[]::new
5 枚举类
枚举定义:类的对象只有有限个 ,确定的。这样的类,我们通常定义为枚举类
5.1枚举类的使用
如何实现:
jdk5 之前 需要自定义枚举类
jdk5开始 enum关键 用于定义枚举类
枚举类如果只有一个对象 此时可以使用单利模式来实现
枚举类的属性:
1 枚举类对象的属性不允许被改动 因此应该使用private final修饰
2 枚举类使用 private final修饰的的属性应该构造器中进行初始化
3 若枚举显式的定义了带参的构造器,在列出枚举值得时候 也必须对应的传入参数
5.2自定义枚举类
1 私有化类的构造器
2 在类的内部创建枚举类的对象
3 对象如果有实例变量,应该声明为private final 并在构造器中初始化
public class Season { private final String SEASONNAME;//季节名称 private final String SEASONDESC;//季节描述 private Season(String seasonName,String seasonDesc){ this.SEASONNAME = seasonName; this.SEASONDESC = seasonDesc; } public static final Season SPRING = new Season("春天","春暖花开"); public static final Season SUMMER = new Season("夏天","骄阳似火"); public static final Season AUTUMN = new Season("秋天","硕果累累"); public static final Season WINTER = new Season("冬天","白雪皑皑"); }
5.3使用Enum实现枚举类
使用enum定义的类默认继承了java.lang.Enum<E> 不能在使用extends继承其他类
构造方法默认为private
枚举类的所有实例必须在枚举类中显式的列出,对象与对象之间使用逗号分割,结尾使用分号。
所有对象的默认修饰是public static final
枚举类的所有实例对象必须在第一行声明
public enum SeasonEnum { //实例对象 SPRING("春天","春暖花开"), SUMMER("夏天","骄阳似火"), AUTUMN("秋天","硕果累累"), WINTER("冬天","白雪皑皑"); //成员变量 private final String SEASONNAME;//季节名称 private final String SEASONDESC;//季节描述 //构造方法 SeasonEnum(String seasonName, String seasonDesc){ this.SEASONNAME = seasonName; this.SEASONDESC = seasonDesc; } public String getSEASONNAME() { return SEASONNAME; } public String getSEASONDESC() { return SEASONDESC; } } public class SeasonEnumTest { public static void main(String[] args) { SeasonEnum spring = SeasonEnum.SPRING; System.out.println(spring.getSEASONNAME() +"----"+spring.getSEASONDESC()); } }
5.4实现接口的枚举类
public interface Behaviour { void print(); String getInfo(); } public enum Color implements Behaviour{ RED("红色",1), GREEN("绿色",2), BLUR("蓝色",3); private final String name; private final Integer index; private Color(String name,Integer index){ this.name = name; this.index = index; } @Override public void print() { System.out.println("颜色:" +name + "---索引:" +index); } @Override public String getInfo() { return this.name; } public static void main(String[] agrs){ Color c = Color.RED; String name = c.getInfo(); System.out.println(name); c.print(); } }
6 Junit
使用JUnit需要添加相应的Jar包或者Maven
6.1测试方法常用注解
@Test:标记这是一个测试方法
6.2断言测试
在方法体中添加assertEquals(预期结果,真正执行的结果);
断言方法有以下:
出处:https://www.cnblogs.com/Justhis610/p/15091523.html