JDK8新特性
第一章.函数式编程思想和Lambda表达式定义格式
1.面向对象思想:重点是找对象,调用对象中的方法帮我们去做事儿,侧重点在找对象
2.函数式编程思想:对面向对象思想进一步简化,不注重过程,只注重结果
3.定义格式:
()->{}
():重写方法的参数位置
->:将参数传递到方法体中
{}:重写方法的方法体
public class Test01 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我到达朝鲜了");
}
}).start();
System.out.println("=============================");
new Thread(()-> System.out.println("我到达朝鲜了")).start();
}
}
第二章.Lambda表达式使用前提
1.使用前提:
a.必须有重写方法
b.必须有函数式接口做方法参数传递
函数式接口:接口中必须有且只能有一个抽象方法
检测函数式接口:在接口上面,@FunctionalInterface
第三章.Lambda表达式省略规则
1.重写方法的参数类型可以省略
2.如果重写方法的参数只有一个,所在的小括号可以省略
3.如果方法体中只有一句代码,所在的大括号可以省略
4.如果方法体中只有一句代码,所在的大括号可以省略之外,那一句代码后面的;可以省略
如果带return,return也可以省略
新手怎么写lambda表达式:
1.观察是否是函数式接口做方法参数传递
2.如果是,可以考虑搞lambda表达式
3.先写一个匿名内部类
4.从new接口开始到重写方法的方法名结束,选中,删除,别忘记再删除右半个大括号
5.在重写方法的参数后面,加上->
6.再看看根据省略规则,能不能进一步删除
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + ''' +
", age=" + age +
'}';
}
}
public class Test02 {
public static void main(String[] args) {
ArrayList<Person> list = new ArrayList<>();
list.add(new Person("柳岩",36));
list.add(new Person("涛哥",12));
list.add(new Person("翠儿",60));
/*
Collections.sort(list, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge()-o2.getAge();
}
});
*/
Collections.sort(list, (o1, o2)-> o1.getAge()-o2.getAge());
System.out.println(list);
}
}
第四章.函数式接口
1.定义格式
1.概述:接口中必须有且只能有一个抽象方法
2.注解: @FunctionalInterface
3.定义格式:
@FunctionalInterface
public interface 接口名{
抽象方法
}
2.基本使用
@FunctionalInterface
public interface USB {
void open();
}
public class Test01 {
public static void main(String[] args) {
method(new USB() {
@Override
public void open() {
System.out.println("鼠标开启");
}
});
System.out.println("=======Lambda表达式======");
method(()->{
System.out.println("鼠标开启");
});
System.out.println("=======Lambda表达式最终写法======");
method(()-> System.out.println("鼠标开启"));
}
public static void method(USB usb){
usb.open();
}
}
3.Supplier
1.Supplier接口
java.util.function.Supplier<T>接口,它意味着"供给"->我们想要什么就给什么
2.方法:
T get()
3.需求:
使用Supplier接口作为方法的参数
用Lambda表达式求出int数组中的最大值
/*
供给型接口
*/
public class Test02_Supplier {
public static void main(String[] args) {
method(new Supplier<Integer>() {
@Override
public Integer get() {
int[] arr = {6,4,6,9,4,6};
Arrays.sort(arr);
return arr[arr.length-1];
}
});
System.out.println("==========Lambda表达式===========");
method(()-> {
int[] arr = {6,4,6,9,4,6};
Arrays.sort(arr);
return arr[arr.length-1];
});
}
public static void method(Supplier<Integer> supplier){
Integer result01 = supplier.get();
System.out.println(result01);
}
}
4.Consumer
java.util.function.Consumer<T>->消费型接口
方法:
void accept(T t),意为消费一个指定泛型的数据
/*
消费型接口
*/
public class Test03_Comsumer {
public static void main(String[] args) {
method(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s.toUpperCase());
}
},"abcdefg");
System.out.println("==========Lambda表达式==============");
method((String s)-> {
System.out.println(s.toUpperCase());
},"abcdefg");
System.out.println("==========Lambda表达式最终写法==============");
method(s-> System.out.println(s.toUpperCase()),"abcdefg");
}
public static void method(Consumer<String> consumer,String s){
consumer.accept(s);
}
}
5.Function
java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据
方法:
R apply(T t)根据类型T参数获取类型R的结果
public class Test04_Function {
public static void main(String[] args) {
method(new Function<Integer, String>() {
@Override
public String apply(Integer integer) {
return integer+"";
}
},100);
System.out.println("==============Lambda表达式================");
method((Integer integer)-> {
return integer+"";
},100);
System.out.println("==============Lambda表达式最终写法================");
method(integer-> integer+"",100);
}
public static void method(Function<Integer,String> function,Integer i){
String s = function.apply(i);
System.out.println(s+1);
}
}
6.Predicate
java.util.function.Predicate<T>接口。->判断型接口
boolean test(T t)->用于判断的方法,返回值为boolean型
public class Test05_Predicate {
public static void main(String[] args) {
method(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.length()==5;
}
},"abcdefg");
System.out.println("==========Lambda表达式==========");
method((String s)-> {
return s.length()==5;
},"abcdefg");
System.out.println("==========Lambda表达式最终写法==========");
method(s-> s.length()==5,"abcdefg");
}
public static void method(Predicate<String> predicate,String s){
boolean result = predicate.test(s);
System.out.println("result = " + result);
}
}
第五章.Stream流介绍,以及初体验
public class Test01_Stream {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("张无忌");
list.add("柳岩");
list.add("张翠山");
list.add("涛哥");
list.add("张三丰");
list.add("杨幂");
list.add("张三丰子");
//将姓"张"人名留下->过滤
ArrayList<String> list1 = new ArrayList<>();
for (String s : list) {
if (s.startsWith("张")){
list1.add(s);
}
}
System.out.println(list1);
//获取名字为三个字的
ArrayList<String> list2 = new ArrayList<>();
for (String s : list1) {
if (s.length()==3){
list2.add(s);
}
}
System.out.println(list2);
//输出
for (String s : list2) {
System.out.println(s);
}
System.out.println("======================");
Stream<String> stream = list.stream();
/*stream.filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.startsWith("张");
}
}).filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.length()==3;
}
}).forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});*/
stream.filter(s -> s.startsWith("张")).filter(s -> s.length()==3).forEach(s -> System.out.println(s));
}
}
第六章.Stream的获取
1.针对数组:
static Stream<T> of(T... values)
2.针对集合:
Stream<E> stream()
public class Test02_Stream {
public static void main(String[] args) {
/* 1.针对数组:
static Stream<T> of(T... values)*/
Stream<String> stream1 = Stream.of("越前龙马", "不二周助", "手冢国光", "桃城武");
/* 2.针对集合:
Stream<E> stream()*/
ArrayList<String> list = new ArrayList<>();
list.add("樱木花道");
list.add("流川枫");
list.add("三井寿");
Stream<String> stream2 = list.stream();
}
}
第七章.Stream的方法
1.Stream中的forEach方法:void forEach(Consumer<? super T> action);
forEach : 逐一处理->遍历
void forEach(Consumer<? super T> action);
注意:forEach方法是一个终结方法
Stream流对象一旦使用了forEach,后续Stream流不能再使用了
public class Test03_Stream {
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("越前龙马", "不二周助", "手冢国光", "桃城武");
/* stream1.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});*/
System.out.println("===========Lambda=========");
/*stream1.forEach((String s)->{
System.out.println(s);
});*/
System.out.println("===========Lambda最终写法=========");
stream1.forEach(s-> System.out.println(s));
//stream1.forEach(System.out::println);
}
}
2.Stream中的long count()方法
1.作用:统计元素个数
2注意:
count方法是一个终结方法
public class Test04_Stream {
public static void main(String[] args) {
Stream<String> stream = Stream.of("张三丰", "张无忌", "张翠山", "殷素素", "殷天正", "张无忌他舅");
long count = stream.count();
System.out.println(count);
//stream.forEach(s -> System.out.println(s));//count()也是一个终结方法
}
}
3.Stream中的Stream<T> filter(Predicate<? super T> predicate)方法
1.方法:Stream<T> filter(Predicate<? super T> predicate)方法,返回一个新的Stream流对象
2.作用:过滤
3.注意:过滤 不是 干掉符合条件的元素;而是留下符合条件的元素
public class Test05_Stream {
public static void main(String[] args) {
Stream<String> stream = Stream.of("张三丰", "张无忌", "张翠山", "殷素素", "殷天正", "张无忌他舅");
/* Stream<String> stream1 = stream.filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.startsWith("张");
}
});
stream1.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});*/
/*stream.filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.startsWith("张");
}
}).forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});*/
stream.filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));
}
}
4.Stream<T> limit(long maxSize):获取Stream流对象中的前n个元素,返回一个新的Stream流对象
1.Stream<T> limit(long maxSize):获取Stream流对象中的前n个元素,返回一个新的Stream流对象
public class Test06_Stream {
public static void main(String[] args) {
Stream<String> stream = Stream.of("张三丰", "张无忌", "张翠山", "殷素素", "殷天正", "张无忌他舅");
stream.limit(3).forEach(s -> System.out.println(s));
}
}
5.Stream<T> skip(long n): 跳过Stream流对象中的前n个元素,返回一个新的Stream流对象
Stream<T> skip(long n): 跳过Stream流对象中的前n个元素,返回一个新的Stream流对象
public class Test07_Stream {
public static void main(String[] args) {
Stream<String> stream = Stream.of("张三丰", "张无忌", "张翠山", "殷素素", "殷天正", "张无忌他舅");
stream.skip(3).forEach(s -> System.out.println(s));
}
}
6.static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b):两个流合成一个流
1.方法:static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b):两个流合成一个流
public class Test08_Stream {
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("铁甲小宝", "卡布达", "蝎子莱莱", "蜻蜓队长", "鲨鱼辣椒");
Stream<String> stream2 = Stream.of("孙悟空", "孙悟饭", "孙悟天", "贝吉塔", "特南克斯");
Stream.concat(stream1,stream2).forEach(s -> System.out.println(s));
}
}
7.将Stream流变成集合
从Stream流对象转成集合对象,使用Stream接口方法collect
public class Test09_Stream {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("和珅");
list.add("丰申殷德");
list.add("乾隆小女儿");
Stream<String> stream = list.stream();
//将Stream转会集合
List<String> list1 = stream.collect(Collectors.toList());
for (String s : list1) {
System.out.println(s);
}
}
}
8.Stream流练习
1. 第一个队伍只要名字为3个字的成员姓名;//filter
2. 第一个队伍筛选之后只要前3个人;//limit
3. 第二个队伍只要姓张的成员姓名;//filter
4. 第二个队伍筛选之后不要前2个人;//skip
5. 将两个队伍合并为一个队伍;//concat
6. 打印整个队伍的姓名信息。//forEach
public class Test10_Stream {
public static void main(String[] args) {
List<String> one = new ArrayList<>();
one.add("迪丽热巴");
one.add("宋远桥");
one.add("苏星河");
one.add("老子");
one.add("庄子");
one.add("孙子");
one.add("洪七公");
List<String> two = new ArrayList<>();
two.add("古力娜扎");
two.add("张无忌");
two.add("张三丰");
two.add("赵丽颖");
two.add("张二狗");
two.add("张天爱");
two.add("张三");
//将两个集合变成Stream流
Stream<String> streamA = one.stream();
Stream<String> streamB = two.stream();
/* Stream<String> stream1 = streamA.filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.length() == 3;
}
}).limit(3);
Stream<String> stream2 = streamB.filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.startsWith("张");
}
}).skip(2);
Stream.concat(stream1,stream2).forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});*/
//Stream<String> stream1 = streamA.filter(s -> s.length() == 3).limit(3);
//Stream<String> stream2 = streamB.filter(s -> s.startsWith("张")).skip(2);
//Stream.concat(stream1,stream2).forEach(s -> System.out.println(s));
Stream.concat(streamA.filter(s -> s.length() == 3).limit(3),streamB.filter(s -> s.startsWith("张")).skip(2)).forEach(s -> System.out.println(s));
}
}
第八章.方法引用
一.方法引用的介绍
1.概述:在Lambda表达式的基础上再次简化
2.注意:使用场景比较局限
3.使用场景:
a.方法引用代码必须在重写的方法中写
b.引用的方法要和所在的重写的方法从参数上,返回值上一样(类型 , 个数)
4.方法引用:
在lambda表达式的基础上,干掉重写方法的参数和箭头以及引用方法的参数
将.换成::
二.方法引入的体验
public class Test01 {
public static void main(String[] args) {
method(new Supplier<String>() {
@Override
public String get() {
/*
1.我们要引用toUpperCase()方法,正好在重写的get方法中
2.重写的get方法,没有参数,有返回值,返回值类型为String
3.我们要引用的toUpperCase,没有参数,有返回值,返回值类型为String
4.结论:可以使用方法引用,引用toUpperCase()
*/
return "abcdef".toUpperCase();
}
});
System.out.println("=========Lambda表达式========");
method(()-> "abcdef".toUpperCase());
System.out.println("=========方法引用========");
method("abcdef"::toUpperCase);
System.out.println("==========================================");
method(new Supplier<String>() {
/*
1.我们要引用substring()方法,正好在重写的get方法中
2.重写的get方法,没有参数,有返回值,返回值类型为String
3.我们要引用的substring,有参数,有返回值,返回值类型为String
4.结论:不可以使用方法引用引用subString方法
*/
@Override
public String get() {
return "abcdefg".substring(0);
}
});
//method("abcdefg"::substring);
}
public static void method(Supplier<String> supplier){
String s = supplier.get();
System.out.println(s);
}
}
三.对象名--引用成员方法
1.使用对象名引用成员方法
格式:
对象::成员方法名
2.需求:
函数式接口:Supplier
java.util.function.Supplier<T>接口
抽象方法:
T get()。用来获取一个泛型参数指定类型的对象数据。
Supplier接口使用什么泛型,就可以使用get方法获取一个什么类型的数据
public class Test02 {
public static void main(String[] args) {
method(new Supplier<String>() {
/*
1.要引用的trim()在重写的get方法中
2.get()无参数,有String类型的返回值
3.引用的trim()无参数,有String类型的返回值
4.trim方法符合方法引用的条件
*/
@Override
public String get() {
return " abcdefg ".trim();
}
});
System.out.println("=========Lambda表达式==========");
method(()->" abcd ".trim());
System.out.println("==========方法引用===========");
method(" abcd "::trim);
}
public static void method(Supplier<String> supplier){
String s = supplier.get();
System.out.println("s = " + s);
}
}
四.类名--引用静态方法
类名--引用静态方法
格式:
类名::静态成员方法
public class Test03 {
public static void main(String[] args) {
method(new Supplier<Double>() {
/*
1.要引用的random()在重写的get方法中
2.get方法无参数,有返回值,返回值类型为Double
3.引用的random()方法,无参数,有返回值,返回值为Double
*/
@Override
public Double get() {
return Math.random();
}
});
System.out.println("===========Lambda=======");
method(() -> Math.random());
System.out.println("===========方法引用===========");
method(Math::random);
}
public static void method(Supplier<Double> supplier){
Double aDouble = supplier.get();
System.out.println("integer = " + aDouble);
}
}
五.类--构造引用
1. 类--构造方法引用
格式:
构造方法名称::new
2.需求:
函数式接口:Function
java.util.function.Function<T,R>接口
抽象方法:
R apply(T t),根据类型T的参数获取类型R的结果。用于数类型转换
public class Test04 {
public static void main(String[] args) {
method(new Function<String, Person>() {
/*
1.Person的有参构造在重写的apply方法中
2.apply方法,带参数的,返回值类型为Person类型
3.Person的有参构造,带参数,返回值类型Person类型
4.可以做方法引用
*/
@Override
public Person apply(String s) {
return new Person(s);//Person p = new Person(s)
}
},"涛哥");
System.out.println("==========Lambda==========");
method(s-> new Person(s),"涛哥");////Person p = new Person(s)
System.out.println("=========方法引用===========");
method(Person::new,"涛哥");
}
public static void method(Function<String,Person> function,String name){
Person person = function.apply(name);
System.out.println(person.getName());
}
}
六.数组--数组引用
数组--数组引用
格式:
数组的数据类型[]::new
int[]::new 创建一个int类型的数组
double[]::new 创建一个double类型的数组
public class Test05 {
public static void main(String[] args) {
method(new Function<Integer, int[]>() {
/*
1.引用的数组在重写的方法中
2.apply有参数,返回值类型为int[]
3.引用的数组,把长度看做是参数,类型是int[]
4.结论:可以使用方法引用
*/
@Override
public int[] apply(Integer integer) {
/*
[] 看做是方法的 ()
[]中的长度看做是方法的参数
*/
return new int[integer];
}
},5);
System.out.println("===============Lambda表达式==============");
method(integer-> new int[integer],5);
System.out.println("===============方法引用==============");
method(int[]::new,5);
}
public static void method(Function<Integer,int[]> function,int length){
int[] arr = function.apply(length);
System.out.println(arr.length);
}
}
第九章 Junit单元测试
1.Junit介绍
1.概述:Junit是一个Java语言的单元测试框架,简单理解为在一定程度上用于取代main方法,Junit属于第三方工具,需要导入jar包后再使用
2.作用:可以单独去运行一个我们想要测试的功能,不用提供main方法了
3.导jar包:
a.在模块下创建文件夹:lib
b.将要使用的第三方jar包,粘贴到lib下
c.对着lib,右键->add as library->module level ->ok
2.Junit的基本使用(重点)
1.注意:需要在方法上写,不要在类上写
@Test
2.怎么执行单元测试的方法:
a.选中要执行的方法名,右键->run
b.选择方法左边的绿色小箭头->run
c.如果想要执行当前类中所有带@Test的单元测试方法,点类名左边绿色小箭头->run
public class Test01_Junit {
@Test
public void add(){
System.out.println("添加商品");
}
@Test
public void delete(){
System.out.println("删除商品");
}
}
3.Junit的注意事项
1.@Test修饰的方法,不能有参数
2.@Test修饰的方法,不能有返回值
3.@Test修饰的方法,不能是静态的
一般情况下,我们都会单独定义一个方法,在此方法中使用单元测试,在方法体中调用要测试的方法
注意:使用单元测试,如果控制台是红色,证明测试的方法有问题,要是绿色的没问题
4.Junit相关注解
@Before:在@Test之前执行,有多少个@Test一起执行,@Before就会执行多少次->可以用初始化参数
@After:在@Test之后执行,有多少个@Test一起执行,@After就会执行多少次->可以用来关闭资源
public class Test02_Junit {
@Test
public void test01() {
System.out.println("单元测试Test");
}
@Test
public void test02() {
System.out.println("单元测试Test");
}
@Before
public void before01() {
System.out.println("单元测试Before");
}
@After
public void after01() {
System.out.println("单元测试After");
}
}
扩展注解:
@BeforeClass:在@Test之前执行,用于修饰静态方法,只执行一次
@AfterClass:在@Test之后执行,用于修饰静态方法,只执行一次
public class Test02_Junit {
@Test
public void test01() {
System.out.println("单元测试Test");
}
@Test
public void test02() {
System.out.println("单元测试Test");
}
@BeforeClass
public static void before01() {
System.out.println("单元测试BeforeClass");
}
@AfterClass
public static void after01() {
System.out.println("单元测试AfterClass");
}
}
第十章.类的加载时机
1.new对象的时候
2.new子类对象(new子类对象,先初始化父类对象)
3.执行main方法
4.调用静态成员
5.利用反射,反射这个类
1.类加载器(了解)_ClassLoader
1.概述:
在jvm中,负责将本地上的class文件加载到内存的对象_ClassLoader
2.分类:
BootStrapClassLoader:根类加载器->C语言写的,我们是获取不到的
也称之为引导类加载器,负责Java的核心类加载的
比如:System,String等
jre/lib/rt.jar下的类都是核心类
ExtClassLoader:扩展类加载器
负责jre的扩展目录中的jar包的加载
在jdk中jre的lib目录下的ext目录
AppClassLoader:系统类加载器
负责在jvm启动时加载来自java命令的class文件(自定义类),以及classPath环境变量所指定的jar包(第三方jar包)
不同的类加载器负责加载不同的类
3.三者的关系:AppClassLoader的父类加载器是ExtClassLoader
ExtClassLoader的父类加载器是BootStrapClassLoader
但是:他们从代码级别上来看,没有子父类继承关系->他们都有一个共同的父类->ClassLoader
4.获取类加载器对象
类名.class.getClassLoader()
5.获取类加载器对象对应的父类加载器
ClassLoader类中的方法:ClassLoader
6.双亲委派(全盘负责委托机制)
a.Person类中有一个String
Person本身是AppClassLoader加载
String是BootStrapClassLoader加载
b.加载顺序:
Person本身是App加载,按道理来说String也是App加载
但是App加载String的时候,先问一问Ext,说:Ext你加载这个String吗?
Ext说:我不加载,我负责加载的是扩展类,但是app你别着急,我问问我爹去->boot
Ext说:boot,你加载String吗?
boot说:正好我加载核心类,行吧,我加载吧!
=======================================================================
比如:
class Test{
new Person()
}
a.Test是app加载,person按理来说也是app加载,但是app先问ext要不要加载
ext说不负责加载自定义类,我找boot去,boot一看,我不负责加载自定义类->perosn
app一看,两个爹都不加载,我自己加载
b.结论:两个双亲都不加载,app才自己加载
比如:如果来了一个DNSNameService,我就想直接加载DNSNameService(扩展类),
本身ext要加载,但是先问boot,如果boot不加载,ext再加载
=======================================================================
7.类加载器的cache(缓存)机制(扩展):一个类加载到内存之后,缓存中也会保存一份儿,后面如果再使用此类,如果缓存中保存了这个类,就直接返回他,如果没有才加载这个类.下一次如果有其他类在使用的时候就不会重新加载了,直接去缓存中拿,所以这就是为什么每个类只加载一次,内存中只有一份儿的原因
8.所以:类加载器的双亲委派和缓存机制共同造就了加载类的特点:每个类只在内存中加载一次
public class Test01 {
public static void main(String[] args) {
app();
//ext();
//bootStrap();
}
/**
* BootStrapClassLoader用于加载核心类 rt.jar
*
* BootStrapClassLoader是C语言写的,获取不到
*/
private static void bootStrap() {
ClassLoader classLoader = String.class.getClassLoader();
System.out.println(classLoader);//null
}
/**
* ExtClassLoader用于加载扩展类
*/
private static void ext() {
ClassLoader classLoader = DNSNameService.class.getClassLoader();
System.out.println(classLoader);
}
/**
AppClassLoader用于加载自定义类和第三方jar包
类名.class.getClassLoader()
*/
private static void app() {
ClassLoader classLoader = Test01.class.getClassLoader();
System.out.println(classLoader);
ClassLoader parent1 = classLoader.getParent();
System.out.println(parent1);
ClassLoader parent2 = parent1.getParent();
System.out.println(parent2);
}
}
第十一章.反射
1.class类的以及class对象的介绍以及反射介绍
小结:
类加载过程
要知道什么是Class对象,什么是class类
class对象:jvm会为加载到内存中的class文件在堆中创建一个对象,此对象为class对象
class类:描述这个class对象的类就是class类
反射:
解剖class对象
我们要先获取class对象,才能解剖class对象
怎么解剖
获取class对象中的成员变量,赋值
获取class对象中的构造方法,new
获取class对象中的成员方法,调用
2.反射之获取Class对象
1.使用Object类中的方法
Class<?> getClass()
2.在java中每个类型(基本类型,引用类型)java都为其提供了一个静态的属性:class
3.使用Class类中的静态方法:
static Class<?> forName(String className)
className:要获取的类的"全限定名"
所谓的全限定名其实就是 包名.类名
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Test01_GetClass {
public static void main(String[] args)throws Exception {
/*
1.使用Object类中的方法
Class<?> getClass()
*/
Person person = new Person();
Class aClass1 = person.getClass();
System.out.println(aClass1);
System.out.println("===========================");
/*
2.在java中每个类型(基本类型,引用类型)java都为其提供了一个静态的属性:class
*/
Class aClass2 = Person.class;
System.out.println(aClass2);
System.out.println("===========================");
/*
3.使用Class类中的静态方法:
static Class<?> forName(String className)
className:要获取的类的"全限定名"
所谓的全限定名其实就是 包名.类名
*/
Class aClass3 = Class.forName("com.atguigu.c_reflect.Person");
System.out.println(aClass3);
System.out.println("===========================");
System.out.println(aClass1==aClass2);//true
System.out.println(aClass1==aClass3);//true
}
}
2.1.三种获取Class对象的方式最常用的一种
使用Class类中的静态方法:
static Class<?> forName(String className)
className:要获取的类的"全限定名"
所谓的全限定名其实就是 包名.类名
因为参数为String,可以结合properties文件动态获取Class对象
className=com.atguigu.c_reflect.Person
public class Test02_GetClass {
public static void main(String[] args)throws Exception {
//创建Properties集合
Properties properties = new Properties();
//创建FileInputStream
FileInputStream in = new FileInputStream("day23\pro.properties");
//调用load方法,将流中的数据加载到properties集合中
properties.load(in);
//System.out.println(properties);
String className = properties.getProperty("className");
Class<?> aClass = Class.forName(className);
System.out.println(aClass);
}
}
3.获取Class对象中的构造方法
3.1.获取所有public的构造方法
Constructor<?>[] getConstructors() -> 获取所有带public的构造方法
public class Test03_GetConstructor {
public static void main(String[] args)throws Exception {
//获取Person的class对象
Class<?> aClass = Class.forName("com.atguigu.c_reflect.Person");
//获取所有public的构造方法
Constructor<?>[] constructors = aClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
}
}
3.2.获取空参构造_public
1.Constructor<T> getConstructor(Class<?>... parameterTypes)
parameterTypes:可变参数,可以传递0或者多个参数
如果获取的是空参构造,parameterTypes不写
如果获取的是有参构造,parameterTypes要写,写的是参数类型的Class对象
2.Constructor类中的方法:创建对象
T newInstance(Object... initargs)
initargs:可变参数,用来创建对象时为属性赋值
如果根据无参构造创建对象,initargs不写
如果根据有参构造创建对象,initargs传递具体的值
public class Test04_GetConstructor {
public static void main(String[] args)throws Exception {
//获取Person的class对象
Class<?> aClass = Class.forName("com.atguigu.c_reflect.Person");
Constructor<?> constructor = aClass.getConstructor();
/*
2.Constructor类中的方法:创建对象
T newInstance(Object... initargs)
initargs:可变参数,用来创建对象时为属性赋值
如果根据无参构造创建对象,initargs不写
如果根据有参构造创建对象,initargs传递具体的值
*/
Object o = constructor.newInstance();//相当于 Person p = new Person()
System.out.println(o);//相当于直接输出p,默认调用toString方法
}
}
3.3.利用空参构造创建对象的快捷方式_public
1.Class类中的方法
T newInstance()-> 根据无参构造创建对象
2.使用前提:被反射的类中必须有public的无参构造
public class Test05_GetConstructor {
public static void main(String[] args)throws Exception {
//获取Person的class对象
Class<?> aClass = Class.forName("com.atguigu.c_reflect.Person");
Object o = aClass.newInstance();//Person p = new Person()
System.out.println(o);//直接输出p,默认调用toString方法
}
}
3.4.利用反射获取有参构造并创建对象_public
1.Constructor<T> getConstructor(Class<?>... parameterTypes)
parameterTypes:可变参数,可以传递0或者多个参数
如果获取的是空参构造,parameterTypes不写
如果获取的是有参构造,parameterTypes要写,写的是参数类型的Class对象
2.Constructor类中的方法:创建对象
T newInstance(Object... initargs)
initargs:可变参数,用来创建对象时为属性赋值
如果根据无参构造创建对象,initargs不写
如果根据有参构造创建对象,initargs传递具体的值
public class Test06_GetConstructor {
public static void main(String[] args)throws Exception {
//获取Person的class对象
Class<?> aClass = Class.forName("com.atguigu.c_reflect.Person");
Constructor<?> constructor = aClass.getConstructor(String.class, int.class);
Object o = constructor.newInstance("柳岩", 36);//Person p = new Person("柳岩",36)
System.out.println(o);//相当于直接输出对象p.默认调用toString
}
}
3.5.利用反射获取私有构造(暴力反射)
Constructor<?>[] getDeclaredConstructors() -> 获取所有构造,包括public,以及private
public class Test07_GetConstructor {
public static void main(String[] args)throws Exception {
//获取Person的class对象
Class<?> aClass = Class.forName("com.atguigu.c_reflect.Person");
Constructor<?>[] dc = aClass.getDeclaredConstructors();
for (Constructor<?> constructor : dc) {
System.out.println(constructor);
}
}
}
1.Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) -> 获取有参构造,包括public,包括private
parameterTypes:可变参数,可以传递0或者多个参数
如果获取的是空参构造,parameterTypes不写
如果获取的是有参构造,parameterTypes要写,写的是参数类型的Class对象
2.Constructor类中的方法:创建对象
T newInstance(Object... initargs)
initargs:可变参数,用来创建对象时为属性赋值
如果根据无参构造创建对象,initargs不写
如果根据有参构造创建对象,initargs传递具体的值
3.Constructor有一个父类:AccessibleObject
AccessibleObject中有一个方法:
void setAccessible(boolean flag)
flag:true->解除私有权限->暴力反射
public class Test07_GetConstructor {
public static void main(String[] args)throws Exception {
//获取Person的class对象
Class<?> aClass = Class.forName("com.atguigu.c_reflect.Person");
Constructor<?> dc = aClass.getDeclaredConstructor(String.class);
dc.setAccessible(true);
Object o = dc.newInstance("柳岩");//Person p = new Person("柳岩")
System.out.println(o);
}
}
4.反射方法
4.1.利用反射获取所有成员方法
1.Method[] getMethods() -> 获取所有public的成员方法->父类的也能获取出来
public class Test08_GetMethod {
public static void main(String[] args)throws Exception {
//获取Person的class对象
Class<?> aClass = Class.forName("com.atguigu.c_reflect.Person");
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
}
}
4.2.反射之获取方法(有参,无参)
1. Method getMethod(String name, Class<?>... parameterTypes)
name:方法名
parameterTypes:可变参数,可以传递0或者多个参数
如果获取的是空参成员方法,parameterTypes不写
如果获取的是有参成员方法,parameterTypes要写,写的是参数类型的Class对象
2.Method类中的方法:
Object invoke(Object obj, Object... args) -> 调用方法并执行
obj:传递对象-> 根据class中的构造创建的对象
args:调用方法是传递的实参
如果调用的方法是无参的,args不写了
如果调用的方法是有参的,args就需要传递实参
Object:用于接收被调用方法的返回值
如果被调用的方法无返回值,Object不用接收了
public class Test09_GetMethod {
public static void main(String[] args)throws Exception {
//获取Person的class对象
Class<?> aClass = Class.forName("com.atguigu.c_reflect.Person");
//利用无参构造创建对象
Object o = aClass.newInstance();//Person p = new Person()
Method setName = aClass.getMethod("setName", String.class);
//执行set方法
setName.invoke(o, "柳岩");//p.setName("柳岩");
System.out.println(o);//直接输出p.默认调用toString
System.out.println("============================");
Method getName = aClass.getMethod("getName");
//执行getName
Object o1 = getName.invoke(o);
System.out.println(o1);
}
}
5.反射练习(编写一个小框架)
<select id = "selectAll">
select * from category
</select>
List<Category> selectAll()
利用反射,解析配置文件中的信息:properties文件
类的全限定名: className=包名.类名
方法名: methodName=方法名
需求:利用反射解析properties文件,获取配置信息
根据配置信息,执行配置文件中的方法
步骤:
1.创建Properties配置文件,写配置信息
问题1:properties配置文件应该放到哪里
a.如果将properties配置文件放到模块下, 那么当我们给用户out路径下的class文件时, out路径下就没有properties配置文件,也没有src路径,那么用户执行程序,程序所需要的配置文件中的数据就拿不到,用户那边也执行不了
b.所以我们一般都是将配置文件放到src下,那么out路径下就有这个配置文件了
问题2:将配置文件放到src下,读取此文件的时候路径怎么写?
FileInputStream in = new FileInputStream("模块名\src\文件名")
如果源码中路径带了src了,由于给用户的out路径下没有src,那么文件到用户那边也读不到
解决问题2: 使用类加载器读取配置文件
当前类名.class.getClassLoader().getResourceAsStream("文件名")->自动扫描src下的配置文件
2.解析配置文件
3.根据配置文件中的类的全限定名,获取Class对象
4.根据配置文件中的方法名获取对应的方法
5.调用invoke执行
className=com.atguigu.c_reflect.Student
methodName=study
public class Test10_Exam {
public static void main(String[] args)throws Exception {
//1.创建Properties配置文件
Properties properties = new Properties();
//2.读取配置文件
//FileInputStream in = new FileInputStream("day23\src\test.properties");
/*
ClassLoader classLoader = Test10_Exam.class.getClassLoader();
InputStream in = classLoader.getResourceAsStream("test.properties");
*/
InputStream in = Test10_Exam.class.getClassLoader().getResourceAsStream("test.properties");
//3.调用load方法,将流中的数据加载到properties集合中
properties.load(in);
//4.获取集合中的value
String className = properties.getProperty("className");//类的全限定名
String methodName = properties.getProperty("methodName");//方法名
//5.获取Class对象
Class<?> aClass = Class.forName(className);
Object o = aClass.newInstance();
//6.根据获取出来的方法名获取方法
Method method = aClass.getMethod(methodName);
//7.执行方法
method.invoke(o);
}
}
第十二章.注解
1.注解的介绍
1.jdk1.5版本的新特性->一个引用数据类型
和类,接口,枚举是同一个层次的
2.作用:
说明:对代码进行说明,生成doc文档(API文档)(不会用)
检查:检查代码是否符合条件 @Override(会用) @FunctionalInterface
分析:对代码进行分析,起到了代替配置文件的作用(会用)
3.JDK中的注解:
@Override -> 检测此方法是否为重写方法
jdk1.5版本,支持父类的方法重写
jdk1.6版本,支持接口的方法重写
@Deprecated -> 方法已经过时,不推荐使用
调用方法的时候,方法上会有横线,但是能用
@SuppressWarnings->消除警告 @SuppressWarnings("all")
public class Person {
@Deprecated
public void eat(){
System.out.println("人要吃饭");
}
}
@SuppressWarnings("all")
public class Test01 {
public static void main(String[] args) {
Person person = new Person();
person.eat();
ArrayList list = new ArrayList();
list.add(1);
}
}
2.注解的定义以及属性的定义格式
1.注解的定义格式:
public @interface 注解名{}
2.定义属性:提高注解的作用
数据类型 属性名() -> 没有默认值,需要在使用注解的时候为其赋值
数据类型 属性名() default 值-> 属性有默认值,在使用注解的时候,无序给该属性赋值;如果需要重新赋值,那就赋呗
3.注解中能够定义什么类型的属性呢?
a.8中基本类型(byte short int long float double boolean char)
b.String类型,Class类型,枚举类型,注解类型
c.以及以上类的一维数组
public @interface Book {
//书名
String name();
//价格
int price();
//作者
String[] author();
//数量,有默认值
int count() default 10;
}
3.注解的使用(重点)
1.所以的使用注解:为注解中的属性赋值
2.使用的位置:类上,方法上,成员变量上,局部变量上,参数上
3.如何使用注解:
@注解名(属性名 = 属性值,属性名 = 属性值...)
4.如果属性是数组:
@注解名(属性名 = {元素1,元素2...})
public @interface Book {
//书名
String name();
//价格
int price();
//作者
String[] author();
//数量
int count() default 10;
}
@Book(name = "金瓶梅",price = 9,author = {"涛哥","柳岩"},count = 100)
public class BookSelf {
}
注解注意事项:
1.空注解可以直接使用->空注解就是注解中没有任何的属性
2.不同的位置可以使用一样的注解,但是同样的位置不能使用一样的注解
3.使用注解时,如果此注解中有属性,注解中的属性一定要赋值,如果有多个属性,用,隔开
如果注解中的属性有数组,那么如果数组只有一个元素值,那么{}不用写,反之用写
4.如果注解中的属性值有默认值,那么我们不必要写,也不用重新赋值,反之必须写上
5.如果注解中只有一个属性,并且属性名叫value,那么使用注解的时候,属性名不用写,直接写值
(包括单个类型,还包括数组)
4.注解解析的方法->AnnotatedElement接口
解析注解:获取注解中的属性值
AnnotatedElement接口的实现类:Class Method Constructor等-> 解析注解离不开反射
接口:AnnotatedElement->专门用于注解解析的
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
判断当前Class对象上是否有指定的注解,如果返回false,证明没注解,否则证明有注解
参数annotationClass:注解的class对象
比如:判断BookSelf上面有没有使用Book注解
Class bookSelf = BookSelf.class;
bookSelf.isAnnotationPresent(Book.class)
==========================================================================
<T extends Annotation> T getAnnotation(Class<T> annotationClass) ->获取当前Class对象上的指定注解
参数annotationClass:注解的class对象
比如:
Class bookSelf = BookSelf.class;
boolean result = bookSelf.isAnnotationPresent(Book.class)
如果bookSelf上使用了Book注解了,就获取
Book book = bookSelf.getAnnotation(Book.class)
=============================================================================
注解的解析思想:
解析类上的注解:
1.反射带有注解的类,获取类的Class对象
2.判断这类上有没有注解:isAnnotationPresent
3.获取这个注解:getAnnotation
4.获取注解中的属性值
解析方法上的注解
1.反射带有注解的类->Class对象
2.获取方法:Method
3.判断方法上有没有注解:method.isAnnotationPresent
4.获取这个方法上的注解:method.getAnnotation
5.获取注解中的属性值
public @interface Book {
//书名
String name();
//价格
int price();
//作者
String[] author();
//数量
int count() default 10;
}
@Book(name = "金瓶梅",price = 9,author = {"涛哥","柳岩"},count = 100)
public class BookSelf {
}
public class Test01 {
public static void main(String[] args) {
//1.获取BookSelf的Class对象
Class<BookSelf> bookSelfClass = BookSelf.class;
//2.判断BookSelf上面有没有注解
boolean result = bookSelfClass.isAnnotationPresent(Book.class);
System.out.println(result);//false
//如果b是true,证明BookSelf上有Book注解,那么我们就获取这个注解
if (result){
//获取注解
Book book = bookSelfClass.getAnnotation(Book.class);
//获取注解中的属性值
System.out.println(book.name());
System.out.println(book.price());
System.out.println(book.count());
System.out.println(Arrays.toString(book.author()));
}
}
}
以上代码没有解析出来
涛哥猜想:如果Book注解在内存中出现了,我们是能判断出来的;但是BookSelf上明明用了Book注解了,但是就是判断不出来,也就证明Book注解有可能没有在内存中出现
第十三章.元注解
1.概述:管理注解的注解
2.元注解在哪些方面去管理注解呢?
a.元注解可以控制自定义注解能写在什么位置上
b.元注解可以控制自定义注解的生命周期
元注解控制自定义注解是否能在源码中,是否能在class文件中,是否能在内存中
3.常见的元注解:
@Target-> 控制自定义注解可以写在什么位置上
a.Target中的属性:ElementType[] value();
b.ElementType是一个枚举,枚举中的属性都是静态的,我们可以类名直接调用
TYPE:自定义注解可以用在类上
FIELD:自定义注解可以用在成员变量上
METHOD:自定义注解可以用在方法上
PARAMETER:自定义注解可以用在参数上
CONSTRUCTOR:自定义注解可以用在构造上
LOCAL_VARIABLE:自定义注解可以用在局部变量上
@Retention->元注解可以控制自定义注解的生命周期
a.Retention属性: RetentionPolicy value();
b.RetentionPolicy是一个枚举,枚举中的成员都是静态,可以类名直接调用
SOURCE:自定义注解只能在源码中出现(默认)
CLASS:自定义注解能在Class文件中
RUNTIME:自定义注解能在内存中存在
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
//书名
String name();
//价格
int price();
//作者
String[] author();
//数量
int count() default 10;
}
1.注解再次解析(成功了)
public class Test01 {
public static void main(String[] args) {
//1.获取BookSelf的Class对象
Class<BookSelf> bookSelfClass = BookSelf.class;
//2.判断BookSelf上面有没有注解
boolean result = bookSelfClass.isAnnotationPresent(Book.class);
System.out.println(result);
//如果b是true,证明BookSelf上有Book注解,那么我们就获取这个注解
if (result){
//获取注解
Book book = bookSelfClass.getAnnotation(Book.class);
//获取注解中的属性值
System.out.println(book.name());
System.out.println(book.price());
System.out.println(book.count());
System.out.println(Arrays.toString(book.author()));
}
}
}
public class Test02 {
public static void main(String[] args) {
//1.获取BookSelf的Class对象
Class bookSelfClass = BookSelf.class;
//2.判断BookSelf上面有没有注解
boolean result = bookSelfClass.isAnnotationPresent(Book.class);
System.out.println(result);
//如果b是true,证明BookSelf上有Book注解,那么我们就获取这个注解
if (result){
//获取注解
//Annotation annotation = bookSelfClass.getAnnotation(Book.class);
/*
强转
注解的父接口是Annotation接口
*/
Book book = (Book) bookSelfClass.getAnnotation(Book.class);
System.out.println(book);
//获取注解中的属性值
System.out.println(book.name());
System.out.println(book.price());
System.out.println(book.count());
System.out.println(Arrays.toString(book.author()));
}
}
}
第一次解析由于我们自己的注解Book默认是SOURCE,只能在源码中,所以我们没有解析出来
第二次解析由于我们将SOURCE改成了RUNTIME,证明注解Book能在内存中出现了,所以我们就解析出来了