回顾
阻塞和非阻塞的概念
阻塞:方法在没有成功的情况下会一直等待
非阻塞:方法不管是否成功都不等待直接向下执行
同步和异步的概念
同步:阻塞的方法有返回值类型就是同步写法
异步:阻塞的方法没有返回值类型有回调函数就是异步写法
ServerSocketChannel和SocketChannel实现连接并收发信息
NIO里面客户端发数据
服务端收数据
Selector选择器
select() :与客户端的连接
selectedkeys() :获取集合,集合中存放的是被连接上的服务端对象
keys() :获取集合,集合中存放的是所有被管理的服务端对象
NIO的特点
可以进行非阻塞操作
AIO的特点
可以进行异步操作
1.类加载
1.类的加载
当一个类第一次被使用时,会被加载到方法区,一个类只会被加载一次。
2.类的加载时机
- 创建类的实例。
- 调用类的静态变量,或者为静态变量赋值。
- 调用类的静态方法。
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
- 初始化某个类的子类。
- 直接使用java.exe命令来运行某个主类。
package com.itheima_01;
public class Test01 {
public static void main(String[] args) {
//1.创建类的实例
//Student s = new Student();
//2.调用类的静态变量,或者为静态变量赋值。
//Student.country = "中国";
//3.调用类的静态方法。
//Student.method();
//4.反射方式
//5.初始化某个类的子类
Student s = new Student();
//6.直接使用java.exe命令来运行某个主类
//运行测试类 测试类也会被加载
}
}
3.类加载器
类加载器就是把类加载到内存的工具。
-
启动类加载器(Bootstrap ClassLoader)
-
扩展类加载器(Extension ClassLoader) 在JDK9之后变成了平台类加载器PlatformClassLoader@e73f9ac
-
应用程序类加载器(Application ClassLoader)
-
idea获取类加载器
【应用程序类加载器】 的父类是 【扩展类加载器】 的父类是 【启动类加载器】
package com.itheima_01; public class Test02 { public static void main(String[] args) { //获取加载Test02这个类的类加载器 ClassLoader classLoader = Test02.class.getClassLoader(); System.out.println(classLoader); //AppClassLoader@18b4aac2 //获取它的父类加载器 ClassLoader parent = classLoader.getParent(); System.out.println(parent); //ExtClassLoader@4554617c //获取父类的父类加载器 ClassLoader parent1 = parent.getParent(); System.out.println(parent1); //null } }
2.反射【重点】
1.反射的概念
反射机制的作用是在程序的运行期间,通过类的字节码对象来获取类的成员并操作,(成员方法、成员变量、构造方法、注解)
2.获取字节码对象的三种方式
- 使用类名调用.class属性
- 使用对象调用getClass()方法
- 使用Class的静态方法forName()
package com.itheima_02;
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException {
//1.使用类名调用.class属性
Class c1 = String.class;
Class c2 = Test01.class;
//2.使用对象调用getClass()方法
Class c3 = "abc".getClass();
//3.使用Class的静态方法forName()
Class c4 = Class.forName("java.lang.String");
System.out.println(c1 == c2); //false
System.out.println(c1 == c3); //true
System.out.println(c1 == c4); //true
}
}
3.反射操作构造方法
-
反射获取构造方法
- Class类的方法:
- getConstructor(Class… c) 可以获取某一个构造方法(公共的)
- getConstructors() 可以获取所有的构造方法(公共的)
- Class类的方法:
-
反射执行构造方法
-
Constructor类的方法:
-
newInstance(Object… obj) : 执行当前构造方法创建对象。
Object… obj 表示的是创建对象时的实际参数。
-
package com.itheima_02; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; public class Test02 { public static void main(String[] args) throws Exception { //获取学生类的字节码对象 Class c = Student.class; //可以使用Class对象来获取类中的构造方法 //getConstructor(Class... c) 可以获取某一个构造方法(公共的) //获取空参构造 Constructor con = c.getConstructor(); //获取有参构造 Constructor con1 = c.getConstructor(String.class, int.class); //使用空参构造创建对象 //newInstance(Object... obj) : 执行当前构造方法创建对象。 Object obj1 = con.newInstance(); //向下转型 Student s1 = (Student) obj1; System.out.println(s1); //Student{name='null', age=0} //使用有参构造创建对象 Object obj2 = con1.newInstance("柳岩",38); System.out.println(obj2); //Student{name='柳岩', age=38} //getConstructors() 可以获取所有的构造方法(公共的) Constructor[] cons = c.getConstructors(); System.out.println(Arrays.toString(cons)); //字节码对象有快捷创建对象的方式,能够直接使用空参构造创建对象 //但是这个方法过时了,不建议使用了 Object o = c.newInstance(); System.out.println(o); } }
-
4.反射操作成员方法
-
反射获取成员方法
- Class类的方法:
-
getMethod(String name, Class… c) :获取一个成员方法(公共的)
String name 表示方法名称
Class… c 表示的是方法的参数的类型
-
getMethods() : 获取类中的所有方法(公共的)
-
- Class类的方法:
-
反射执行成员方法
-
Method类的方法:
-
Object invoke(Object obj , Object… o) :让方法执行
第一个参数表示执行的对象
第二个参数表示方法的实际参数
返回值表示方法的实际返回值
-
-
package com.itheima_02;
import java.lang.reflect.Method;
public class Test03 {
public static void main(String[] args) throws Exception {
//获取字节码对象
Class c = Class.forName("com.itheima_02.Student");
//getMethod(String name, Class... c) :获取一个成员方法(公共的)
//第一个参数:方法的方法名。 第二个参数:方法的参数类型
//获取睡觉方法
Method m1 = c.getMethod("sleep");
//获取吃饭方法
Method m2 = c.getMethod("eat", String.class);
//getMethods() : 获取类中的所有方法(公共的)
//包含了自己的方法和父类的方法
Method[] methods = c.getMethods();
//创建学生对象
Student s = new Student("柳岩",38);
//Object invoke(Object obj , Object... o) :让方法执行
//之前的调用:对象,方法,实参
//s.eat("米饭");
//反射的调用:
m2.invoke(s, "米饭");
//执行睡觉方法
m1.invoke(s);
}
}
5.暴力反射
之前的方法只能获取公共的成员,暴力反射的意思就是可以强制获取类中的所有成员。不管是不是public都可以获取到。用法就是在之前的方法中间加一个词Declared
getMethod() getDeclaredMethod()
getConstructor() getDeclaredConstructor()
等等每个方法都有。。。。但是我们一般不要使用,因为他打破了java的语法规范
package com.itheima_02;
import java.lang.reflect.Method;
public class Test04 {
public static void main(String[] args) throws Exception {
//获取字节码对象
Class c = Class.forName("com.itheima_02.Student");
//暴力获取这个方法
Method m2 = c.getDeclaredMethod("eat", String.class);
//设置强制访问
m2.setAccessible(true);
//创建学生对象
Student s = new Student("柳岩",38);
//反射的调用:
m2.invoke(s, "米饭");
}
}
6.反射操作成员变量【了解】
Class类中方法:
getField(String name) : 获取一个成员变量。参数表示成员变量的名称
在类中成员变量都是私有的,建议不要暴力反射直接操作成员变量。
应该调用对应的公共的setget方法。
package com.itheima_02;
import java.lang.reflect.Field;
public class Test05 {
public static void main(String[] args) throws NoSuchFieldException {
//获取字节码对象
Student s = new Student();
Class c = s.getClass();
//getField(String name) :获取成员变量
Field f1 = c.getDeclaredField("name");
System.out.println(f1);
//接下来可以给他进行赋值,但是我们不往下写了,也不会使用这个办法。
}
}
7.反射的作用案例演示
反射是java框架的灵魂。
-
案例演示
学习反射不是为了代码简单。为了更加灵活。
配置文件
cn=com.itheima_03.Dog mn=eat
代码
package com.itheima_03; import java.io.FileReader; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Properties; public class Test { public static void main(String[] args) throws Exception { //不用反射: //看看猫怎么睡觉 //Animal a = new Dog(); //a.sleep(); //properties键值对 //创建对象 Properties p = new Properties(); //加载 p.load(new FileReader("day14\\config.properties")); //根据键获取值 String cn = p.getProperty("cn"); String mn = p.getProperty("mn"); //用反射: //获取字节码对象 Class c = Class.forName(cn); //获取构造方法 Constructor con = c.getConstructor(); //执行构造方法 Animal a = (Animal) con.newInstance(); //获取方法 Method m = c.getMethod(mn); //执行方法 m.invoke(a); } }
3.注解
1.概述
在java中,使用@来表示注解,注解也是类的一个组成部分。
2.注解的作用
-
生成帮助信息
@author 作者介绍 @param 参数介绍
-
编译检查
@Override 检查重写方法 @FunctionalInterface 检查函数式接口
-
功能型
@Test 单元测试
4.自定义注解
1.定义格式
public @interface 注解名{
属性
}
2.注解的属性
-
属性的格式
数据类型 属性名();
-
数据类型
基本数据类型 String Class 枚举 注解 以上类型的数组形式
-
示例
package com.itheima_04; //名叫AAA的注解 public @interface AAA { //数据类型 属性名(); int age(); //基本类型 String name(); //String int[] num(); //数组类型 Class c(); //Class类型 BBB b(); //注解类型 }
3.注意事项【重点】
-
属性可以定义默认值,定义了默认值,就不会要求必须赋值了
int age() default 18; String name() default "柳岩"; int[] num() default {11,22};
-
如果注解中只有一个属性需要赋值,如果属性名叫value,那么在使用注解时,可以省略键的部分
定义: String value(); 使用: @AAA("男")
-
数组形式的属性中如果只有一个元素,那么可以省略大括号
num是一个数组属性 @AAA(value = "男",num=11)
4.元注解
元注解就是给注解加的注解。
@Target
@Target
表示注解的作用位置
ElementType.FIELD 成员变量
ElementType.METHOD 成员方法
ElementType.TYPE 类
还有别的取值我们不用,如果不设置Target默认是在任何位置都能加
@Retention
@Retention
表示注解的存活阶段
RetentionPolicy.SOURCE 源码阶段
RetentionPolicy.CLASS 编译阶段
RetentionPolicy.RUNTIME 运行阶段
如果不写Retention默认会存活到CLASS
5.解析注解的属性值
package com.itheima_04;
import java.lang.annotation.Annotation;
@AAA("柳岩")
public class Test02 {
public static void main(String[] args) {
//反射获取注解
Class c = Test02.class;
//获取类的注解
Annotation anno = c.getAnnotation(AAA.class);
//向下转型
AAA a = (AAA) anno;
//获取属性值
String value = a.value();
System.out.println(value); //柳岩
int age = a.age();
System.out.println(age); //18 是定义的默认值
}
}
6.案例完成测试框架
package com.itheima_04;
import java.util.HashSet;
import java.util.Set;
//假设是我们开发的一个程序,一个程序中有很多代码有很多方法,我们想要测试方法有没有异常的产生
public class MyQQ {
public void method(){
int[] arr = new int[4];
arr[10] = 5;
}
public void method01(){
String s = "";
int length = s.length();
}
public void method02(){
Set<String> set = new HashSet<>();
set.add("张三");
set.add("老王");
set.add("赵四");
for (String s : set) {
if(s.equals("老王")){
set.remove(s);
}
}
}
public void method03(){
String s = null;
int length = s.length();
}
}
package com.itheima_04;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
}
//测试类
public class MyTest {
public static void main(String[] args) throws IOException {
//创建输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("day14\\bug.txt"));
//1.获取MyQQ类的字节码对象
MyQQ qq = new MyQQ();
Class c = qq.getClass();
//2.获取类的所有方法
Method[] methods = c.getMethods();
//定义计数器
int count = 0;
//增强for遍历
for (Method method : methods) {
//3.判断方法是否有Check注解
//4.如果方法有注解,就执行方法
if (method.isAnnotationPresent(Check.class)) {
try {
method.invoke(qq);
} catch (Exception e) {
count++;
//5.如果执行出现异常把异常信息输出到文件中
bw.write("在" + method.getName() + "方法中: 出现了" + e.getCause() + "异常");
bw.newLine();
bw.write("-----------------------");
bw.newLine();
}
}
}
//写出总次数
bw.write("本次测试发现了" + count + "个异常");
//关流
bw.close();
}
}
5.动态代理【重点】
1.作用
动态代理可以对一个类的方法进行动态的增强,利用反射机制在程序的运行期间对方法进行操作。
2.使用前提
类必须要实现接口。
3.ArrayList使用工具类的演示
//集合
List<String> list = new ArrayList<>();
list.add("柳岩");
list.add("崔航");
list.add("奥利给");
//工具类
//返回的是list的代理对象
//代理对象和被代理对象相当于真假对象,这两个对象非常相似,用list2去代替list
//孙悟空list 六耳猕猴list2
//List<String> list2 = Collections.unmodifiableList(list);
4.案例:不允许集合增删改
package com.itheima_05;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@SuppressWarnings("all")
public class Test01 {
public static void main(String[] args) {
//集合
List<String> list = new ArrayList<>();
list.add("柳岩");
list.add("崔航");
list.add("奥利给");
List<String> list2 = method(list);
boolean b = list2.add("石原里美");
}
//我返回的这个集合是一个代理对象
//不让调用增删改方法
public static List<String> method(List<String> list){
/*
三个参数:
ClassLoader loader 类加载器
Class<?>[] interfaces 接口
InvocationHandler h 匿名内部类
返回值:
Object 代理对象
*/
List<String> obj = (List<String>)Proxy.newProxyInstance(list.getClass().getClassLoader(), list.getClass().getInterfaces(), new InvocationHandler() {
@Override
/*
在使用代理对象调用任何方法时,都会进入到invoke方法中
三个参数:
Object proxy 代理对象(在这里不使用他)
Method method 代表被代理对象调用的方法
Object[] args 代表调用方法时传入的实际参数
返回值:
Object 代表执行完方法之后的返回值
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//执行任何方法都会都到这里来
String name = method.getName();
if(name.equals("add") || name.equals("set") || name.equals("remove")){
//判断如果是增删改方法
//直接给他产生异常
throw new RuntimeException("不允许调用增删改方法");
}else{
//判断如果不是增删改方法
//就让他正常执行
Object obj = method.invoke(list, args);
return obj;
}
}
});
return obj;
}
}
5.案例:集合只允许添加四个字的字符串
package com.itheima_05;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("all")
public class Test02 {
public static void main(String[] args) {
//集合
List<String> list = new ArrayList<>();
list.add("柳岩");
list.add("石原里美");
List<String> list2 = method(list);
//list2.add("崔航");
list2.add("新垣结衣");
String s = list2.get(1);
System.out.println(s);
System.out.println(list);
}
//写一个动态代理的方法
//生成一个代理对象,功能代理对象只允许添加四个字的字符串
public static List<String> method(List<String> list){
List<String> o = (List<String>) Proxy.newProxyInstance(list.getClass().getClassLoader(), list.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断是不是添加方法
if(method.getName().equals("add")){
//要求:参数的长度必须是4
String s = (String) args[0];
if(s.length() != 4){
throw new RuntimeException("只允许添加长度为4的字符串!!!");
}
}
//其他方法都正常执行
Object obj = method.invoke(list, args);
return obj;
}
});
return o;
}
}
6.Lombok插件
import lombok.*;
@Data
//@Setter
//@Getter
//@ToString
//@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private int age;
}