文章目录
- Day07 IO流
- 1.IO流
- 1.1背景介绍
- 1.2File类
- 1.2.1常用方法
 
- 1.3IO流原理
- 1.4IO流的分类
- 1.4.1InputStream 字节输入流
- 1.4.1.1FileInputStream
- 1.4.1.2FileOutPutStream
- 1.4.1.3练习
 
- 1.4.2Reader and Writer
- 1.4.2.1FileReader
- 1.4.2.2FileWriter
 
- 1.4.3节点流和处理流
- 1.4.3.1处理流的优点
- 1.4.3.2装饰者模式
 
- 1.4.4Buffered
- 1.4.4.1BufferedReader
- 1.4.4.2BufferedWriter
- 1.4.4.3BufferedInputStream
- 1.4.4.4BufferedOutputStream
 
- 1.4.5Object
- 1.4.5.1ObjectInputStream
- 1.4.5.2ObjectOutputStream
 
- 1.4.6标准输入输出流(IO流的实例,不属于IO特定的分类)
- 1.4.7转换流(适用于处理纯文本文件)
- 1.4.7.1InputStreamReader(解码)
- 1.4.7.2OutputStreamWriter(编码)
 
- 1.4.8打印流
- 1.4.8.1PrintStream
- 1.4.8.2PrintWriter
 
- 1.4.9Properties
- 1.4.10练习
- 1.4.11总结
 
 
 
- Day08 多线程
- 1.多线程
- 1.1概念
- 1.2线程的使用
- 1.2.1多线程机制
- 1.2.2start方法
- 1.2.3实现Runnable接口
 
- 1.2.4线程方法
 
- 1.3线程生命周期
- 1.4线程的同步:star::star::star:
- 1.4.1解决方法(Synchronized代码块和方法)
- 1.4.2死锁问题
- 1.4.3Lock锁(线程安全)
 
 
- 1.5线程通信
- 1.6新增线程创建方法(jdk5.0~)
 
 
- Day09 网络编程
- 1.网络编程
- 1.1基础概念
- 1.1.2ip地址
- 1.1.3域名和端口号
- 1.1.4网络协议
- 1.1.5TCP和UDP:star::star::star:
 
- 1.2InetAddress类
- 1.3Socket
- 1.4TCP编程:star::star::star:
- 1.4.1字节流
- 1.4.2字符流
 
- 1.5UDP网络编程
- 1.6URL编程
 
 
- Day10 反射
- 1.反射
- 1.1反射概念和机制
- 1.1.1举例
- 1.1.2理解
- 1.1.2.1反射原理图:star::star::star:
- 1.1.2.2反射功能
 
 
- 1.2反射相关类
- 1.3反射的优点和缺点
- 1.4class类
- 1.4.1class类方法
- 1.4.2获取class类对象的六种方式
 
- 1.5类加载
- 1.5.1类加载时机:star::star::star:
- 1.5.2类加载各个阶段
- 1.5.2.1加载阶段
- 1.5.2.2连接
- 1.5.2.2.1验证阶段
- 1.5.2.2.2准备
- 1.5.2.2.3解析
 
- 1.5.2.3初始化
 
 
- 1.6通过反射获取类的结构信息
- 1.6.1第一组
- 1.6.2第二组(Field)
- 1.6.3第三组(Method)
- 1.6.4第四组(Constructor)
 
- 1.7运用反射机制进行操作
- 1.7.1通过反射创造对象
- 1.7.2通过反射操作属性
- 1.7.3通过反射调用方法
 
- 1.8动态代理:star::star::star:
- 1.8.1实现步骤:star:
- 1.8.1-1
- 1.8.2-2
- 1.8.3-3
- 1.8.4-4
- 1.8.5-5.main方法实现
 
- 1.8.2动态代理与AOP(面向切面编程)
 
 
 
Day07 IO流
1.IO流
1.1背景介绍

1.2File类
-  创建File类的对象,只是在内存中创建了一个对象,并没有写入到磁盘中,只有调用相应的CreateFile()方法,才能写入到磁盘中 
-  文件这个类,按目录构建的时候是一个目录,按目录+文件名构建的时候是一个文件 
-  java编程中目录也是一种文件 
-  File类对象包括: 文件 + 目录 

public class FileCreate {
    public static void main(String[] args) {
    }
    @Test
    //方式一:new File(String pathName)
    public void method1(){
        String path = "text1.txt";
        File file = new File(path);//创建file对象,这时还没创建文件
        try {
            file.createNewFile();
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //方式 2 new File(File parent,String child) //根据父目录文件+子路径构建
    @Test
    public void method2(){
        File file = new File("D:\\desktop\\666\\study\\Java\\javaseReverse");
        String name = "text2.txt";
        File file1 = new File(file, name);
        try {
            file1.createNewFile();
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //方式 3 new File(String parent,String child) //根据父目录+子路径构建
    @Test
    public void method3(){
        String parentPath = "D:\\desktop\\666\\study\\";
        String childPath  = "Java\\javaseReverse\\text3.txt";
        File file = new File(parentPath, childPath);
        try {
            file.createNewFile();
            System.out.println("文件创建成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
1.2.1常用方法

//获取文件信息
    @Test
    public void test1(){
        //加载文件对象
        File file = new File("text1.txt");
        System.out.println(file.getName());
        //调用相应的方法,得到对应信息
        System.out.println("文件名字=" + file.getName());
        //getName、getAbsolutePath、getParent、length、exists、isFile、isDirectory
        System.out.println("文件绝对路径=" + file.getAbsolutePath());
        System.out.println("文件父级目录=" + file.getParent());//这个文件没有指定父目录,是按相对路径创建的,所以返回null
        System.out.println("文件大小(字节)=" + file.length());
        System.out.println("文件是否存在=" + file.exists());//T
        System.out.println("是不是一个文件=" + file.isFile());//T
        System.out.println("是不是一个目录=" + file.isDirectory());//F
    }

//删除文件操作 file.delete()
    @Test
    public void method1(){
        File file = new File("text3.txt");
        if(file.exists()){
            boolean delete = file.delete();
            if(delete){
                System.out.println("文件删除成功");//T
            }else{
                System.out.println("文件删除不成功");
            }
        }else{
            System.out.println("文件不存在");
        }
    }
//删除目录操作 file.delete()
    //目录也是一种特殊的文件
    @Test
    public void method2(){
        File file = new File("e:\\demo");
        if(file.exists()){
            boolean delete = file.delete();
            if(delete){
                System.out.println("目录删除成功");
            }else{
                System.out.println("目录删除不成功");
            }
        }else{
            System.out.println("目录不存在");//T
        }
    }
	//判断目录是否存在,如果不存在创建该目录
	//mkdirs()针对的是目录文件
    @Test
    public void method3(){
        File file = new File("e:\\demo\\a\\b");
        if(file.exists()){
            System.out.println("该目录已存在");
        }else{
            if(file.mkdirs()){创建多级目录
                System.out.println("该目录创建成功");//T
            }else{
                System.out.println("创建失败");
            }
        }
    }
1.3IO流原理
-  原理图 
-  IO流必须借助File对象,对File对象进行修改 可理解为IO流(File),获取对象,进行对文件的操作  

1.4IO流的分类
- 一般用循环读取文件(读取文件注意返回值),然后写入(直接写入就完事了)



1.4.1InputStream 字节输入流
- read:要么一个一个读取,要不创建一个byte数组,多次读取


1.4.1.1FileInputStream


/**
     * 演示读取文件... * 单个字节的读取,效率比较低
     * -> 使用 read(byte[] b)
     */
    @Test
    public void readFile01(){
        String filePath = "text1.txt";
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(filePath);
            int readContext;
            //从该输入流读取一个字节的数据。 如果没有输入可用,此方法将阻止。
            //如果返回-1 , 表示读取完毕
            while((readContext = fis.read()) != -1){
                System.out.println((char) readContext);//中文会乱码
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
- 每次对流进行操作完,关闭流 的根本原因是,文件描述符不属于jvm管理的,是没有办法通过垃圾回收器回收,因此必须手动关闭

/**
     * 
     * -> 使用 read(byte[] b),一次读取好几个,效率较高
     */
@Test
    public void readFile02() {
        String filePath = "text1.txt";
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(filePath);
            byte[] buff = new byte[5];
            int readLen;
            //从该输入流读取最多 b.length 字节的数据到字节数组。 此方法将阻塞,直到某些输入可用。
            //如果返回-1 , 表示读取完毕
            //如果读取正常, 返回实际读取的字节数
            //如果读取的不够5个字节,那么就返回实际读取的字节数
            while ((readLen = fis.read(buff)) != -1) {
                System.out.println(new String(buff,0,readLen));//用String来讲字节数组转换为字符,这里不能直接用byte[]数组
                                                                    //因为最后如果读取小于五个字符,输出的buff数组前一部分为读取的
                                                                    //部分,其他部分为之前的部分,因此每次buff数组只取实际读取的
                                                                    //一部分
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
1.4.1.2FileOutPutStream
- 特殊的一点是:如果文件不存在,那么会新建一个文件
- 构造器,后面加True是追加的形式
- 写入的时候:是以字节的形式进行写入的,因此要调用String.getbytes()方法转换为字节数组再写入


 @Test
    public void writeFile() {
        //创建 FileOutputStream 对象
        String filePath = "text3.txt";
        FileOutputStream fileOutputStream = null;
        try {
            //得到 FileOutputStream 对象 对象
            //老师说明
            //1. new FileOutputStream(filePath) 创建方式,当写入内容是,会覆盖原来的内容
            //2. new FileOutputStream(filePath, true) 创建方式,当写入内容是,是追加到文件后面
            fileOutputStream = new FileOutputStream(filePath, true);
            //写入一个字节
            //fileOutputStream.write('H');//
            //写入字符串
            String str = "hsp,world!";
            //str.getBytes() 可以把 字符串-> 字节数组
            //fileOutputStream.write(str.getBytes());
            /*
            write(byte[] b, int off, int len) 将 len 字节从位于偏移量 off 的指定字节数组写入此文件输出流
            */
            fileOutputStream.write(str.getBytes(), 0, 3);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
1.4.1.3练习

@Test
    public void testCopy(){
        String filePath = "java.jpg";
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //读取文件
            fis = new FileInputStream(filePath);
            fos = new FileOutputStream("javaCopy.jpg");
            //读取到后,就写入到文件 通过 fileOutputStream
            //即,是一边读,一边写
            byte[] buff = new byte[1024];
            int readLen;
            while((readLen = fis.read(buff)) != -1){
                fos.write(buff,0,readLen);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
1.4.2Reader and Writer



1.4.2.1FileReader

@Test
    public void storyReader(){
        String filePath = "story.txt";
        FileReader fr = null;
        try {
            fr = new FileReader(filePath);
            char[] readContext = new char[1024];
            int readCount;
            while((readCount = fr.read(readContext)) != -1){
                System.out.print(new String(readContext,0,readCount));这里一定要用String处理
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(fr != null){
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
1.4.2.2FileWriter
- 在write方法写入完数据之后,一定要flush或者close流,这样才能把数据真正地写入到文件中

public class FileWriter_ {
    public static void main(String[] args) {
    }
    @Test
    public void test(){
        String filePath = "note.txt";
        //创建 FileWriter 对象
        FileWriter fileWriter = null;
        char[] chars = {'a', 'b', 'c'};
        try {
            fileWriter = new FileWriter(filePath);//默认是覆盖写入
            // 3) write(int):写入单个字符
            fileWriter.write('H');
            // 4) write(char[]):写入指定数组
            fileWriter.write(chars);
            // 5) write(char[],off,len):写入指定数组的指定部分
            fileWriter.write("韩顺平教育".toCharArray(), 0, 3);
            // 6) write(string):写入整个字符串
            fileWriter.write(" 你好北京~");
            fileWriter.write("风雨之后,定见彩虹");
            // 7) write(string,off,len):写入字符串的指定部分
            fileWriter.write("上海天津", 0, 2);
        }catch (IOException e){
            e.printStackTrace();
        } finally {
        //对应 FileWriter , 一定要关闭流,或者 flush 才能真正的把数据写入到文件
        //老韩看源码就知道原因. /*
//        看看代码
//        private void writeBytes() throws IOException {
//            this.bb.flip();
//            int var1 = this.bb.limit();
//            int var2 = this.bb.position();
//            assert var2 <= var1;
//            int var3 = var2 <= var1 ? var1 - var2 : 0;
//            if (var3 > 0) {
//                if (this.ch != null) {
//                    assert this.ch.write(this.bb) == var3 : var3;
//                } else {
//                    this.out.write(this.bb.array(), this.bb.arrayOffset() + var2, var3);
//
//                }
//            }
//        }
//        this.bb.clear();
//        }
            try {
            //fileWriter.flush();
            //关闭文件流,等价 flush() + 关闭
            fileWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
1.4.3节点流和处理流
- 之前学过的处理文件的流都是节点流
- 处理流可以套一层节点流,使处理功能更强大
- 区分关键点:如果直接对对象(数据)进行处理,是节点流;有功能名字的流都是处理流







1.4.3.1处理流的优点

1.4.3.2装饰者模式


1.4.4Buffered
1.4.4.1BufferedReader
- 主要方法是跟以前一样还是有read()方法,新增方法readline读取一行,效率更高,读取到文件末尾返回null

public class BufferedReader_ {
    public static void main(String[] args) throws Exception{
        String filePath = "story.txt";
        BufferedReader reader = new BufferedReader(new FileReader(filePath));
        //说明
        //1. bufferedReader.readLine() 是按行读取文件
        //2. 当返回 null 时,表示文件读取完毕
        String line = null;
        while((line = reader.readLine()) != null){
            System.out.println(line);
        }
        //只需关闭外部流即可
        reader.close();
    }
}
1.4.4.2BufferedWriter
- 和以前一样有writer方法,可以按照char[]数组写入,也可以按照String数组写入
public class BufferedWriter_ {
    public static void main(String[] args) throws Exception{
        String filePath = "text2.txt";
        //创建 BufferedWriter
        //说明:
        //1. new FileWriter(filePath, true) 表示以追加的方式写入
        //2. new FileWriter(filePath) , 表示以覆盖的方式写入
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath));
        bufferedWriter.write("hello, 韩顺平教育!");
        bufferedWriter.newLine();//插入一个和系统相关的换行
        bufferedWriter.write("hello2, 韩顺平教育!");
        bufferedWriter.newLine();
        bufferedWriter.write("hello3, 韩顺平教育!");
        bufferedWriter.newLine();
        //说明:关闭外层流即可 , 传入的 new FileWriter(filePath) ,会在底层关闭
        bufferedWriter.close();
    }
}
public class BufferedCopy {
    public static void main(String[] args) throws Exception{
        
        //1. BufferedReader 和 BufferedWriter 是安装字符操作
        //2. 不要去操作 二进制文件[声音,视频,doc, pdf ], 可能造成文件损坏
        String srcFilePath = "story.txt";
        String destFilePath = "storyCopy.txt";
        BufferedReader reader = new BufferedReader(new FileReader(srcFilePath));
        BufferedWriter writer = new BufferedWriter(new FileWriter(destFilePath));
        String readContext = null;
        //readline读取一行的内容,但是不读取换行符
        while ( (readContext = reader.readLine()) != null){
            writer.write(readContext);
            //插入一个换行符,与系统文件相关
            writer.newLine();
        }
        reader.close();
        writer.close();
    }
}
1.4.4.3BufferedInputStream


1.4.4.4BufferedOutputStream


1.4.5Object


1.4.5.1ObjectInputStream
-  反序列化的时候一定要按照序列化的顺序来,否则会报异常 
-  序列化的类和反序列化解析出来的类是同一个类,否则会报异常,因为只有这样解析出来的才能使一个类 
-  实际意义是,都是存放一个类对象的文件,便于读取 
-  序列化对象时,建议添加serialVersionUID,提高版本兼容性 
-  序列化对象默认将里面的对象都序列化,除了static和transient,transient就代表不想序列化 
-  序列化类属性必须实现可序列化接口,才能序列化类  

public class Dog implements Serializable {
    private String name;
    private int age;
    private String country;
    private String color;
    //序列化版本号,提高版本兼容性
    private static final long serialVersionUID = 1L;
    public Dog(String name, int age, String country, String color) {
        this.name = name;
        this.age = age;
        this.country = country;
        this.color = color;
    }
    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", country='" + country + '\'' +
                ", color='" + color + '\'' +
                '}';
    }
    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 String getCountry() {
        return country;
    }
    public void setCountry(String country) {
        this.country = country;
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
}
public class ObjectInputStream_ {
    public static void main(String[] args) throws Exception{
        String filePath = "data.txt";
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
        //反序列化的时候一定要按照序列化的顺序来
        //否则会报异常
        System.out.println(ois.readInt());
        System.out.println(ois.readBoolean());
        System.out.println(ois.readChar());
        System.out.println(ois.readDouble());
        System.out.println(ois.readUTF());
        //这里有一个重要的细节
        //1.如果我们希望调用Dog的方法,需要向下转型
        //2.需要我们将Dog类的定义,方法到可以引用的位置,这样序列化和反序列化出来的类的和引用的位置一致
        //换句话说,就是序列化的类和反序列化解析出来的类是同一个类
        Object obj = ois.readObject();
        Dog dog = (Dog)obj;
        System.out.println(dog);
        System.out.println(dog.getName());
        ois.close();
    }
}
1.4.5.2ObjectOutputStream
- 序列化后的保存的文本的格式不是纯文本的,是一种

public class ObjectOutputStream_ {
    public static void main(String[] args) throws Exception{
        //序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存
        String filePath = "data.txt";
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
        //序列化数据到 e:\data.dat
        oos.writeInt(100);// int -> Integer (实现了 Serializable)
        oos.writeBoolean(true);// boolean -> Boolean (实现了 Serializable)
        oos.writeChar('a');// char -> Character (实现了 Serializable)
        oos.writeDouble(9.5);// double -> Double (实现了 Serializable)
        oos.writeUTF("韩顺平教育");//String
        //保存一个 dog 对象
        oos.writeObject(new Dog("旺财", 10, "日本", "白色"));
        oos.close();
        System.out.println("数据保存完毕(序列化形式)");
    }
}
//如果没有实现Serializable,就会报java.io.NotSerializableException异常
//class Dog implements Serializable {
//    private String name;
//    private int age;
//    private String country;
//    private String color;
//
//    public Dog(String name, int age, String country, String color) {
//        this.name = name;
//        this.age = age;
//        this.country = country;
//        this.color = color;
//    }
//}
1.4.6标准输入输出流(IO流的实例,不属于IO特定的分类)


1.4.7转换流(适用于处理纯文本文件)
- 解决编码问题



1.4.7.1InputStreamReader(解码)
-  本质上就是将字节流转换为字符流,因为InputStreamReader是Reader的子类,得到一个转换过的字符流(reader)罢了 可以在转换后用BufferedReader进行包装,因为BufferedReader可接收Reader及其子类的对象,处理效率更高 


public class InputStreamReader_ {
    public static void main(String[] args) throws Exception{
        String filePath = "text2.txt";
        //解读
        // 1. 把 FileInputStream 转成 InputStreamRea
        //2. 指定解码格式 utf-8
        InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "utf-8");
        BufferedReader bufferedReader = new BufferedReader(isr);
        String str = null;
        while((str = bufferedReader.readLine()) != null){
            System.out.println(str);
        }
        //只用关闭最外层流,即BufferedReader即可
        bufferedReader.close();
    }
}
1.4.7.2OutputStreamWriter(编码)

public class OutputStreamWriter_ {
    public static void main(String[] args) throws Exception{
        String filePath = "编码格式gbk.txt";
        FileOutputStream fos = new FileOutputStream(filePath);
        OutputStreamWriter osw = new OutputStreamWriter(fos, "gbk");
        BufferedWriter bufferedWriter = new BufferedWriter(osw);
        bufferedWriter.write("你好,世界");
        bufferedWriter.close();
    }
}
1.4.8打印流

1.4.8.1PrintStream
-  就两个关键点,一个是构造器(指定传入位置的功能),一个是输出方法 
-  printStream构造器方法可以直接传入一个文件路径(String或者File),这样可以修改输出的位置 
-  这个类的print方法和write方法异曲同工,都是输出信息到指定位置 
-  System.SetOut方法可以直接传入一个指定位置的输出流printStream,进行位置的修改 
-  System.out的输出流输出到控制台上的,因此要指定输出内容到控制台,可传入System.out 


public class PrintStream_ {
    public static void main(String[] args) throws Exception{
        //1.
        PrintStream ps = System.out;
        //在默认情况下PrintStream输出的位置是标准输出,即显示器
        ps.print("hello world");
        //        public void print(String s) {
        //            write(String.valueOf(s));
        //        }
        //因为print底层使用Write方法,所以我们可以直接使用Write进行打印/输出
        ps.write("你好世界".getBytes(StandardCharsets.UTF_8));
        //2.修改打印流的输出位置(设备)
        System.setOut(new PrintStream("print.txt"));
        System.out.print("你好世界");
        ps.close();
        
    }
}
1.4.8.2PrintWriter

public class PrintWriter_ {
    public static void main(String[] args) throws Exception{
        //输出方式1
//        PrintWriter printWriter = new PrintWriter(System.out);//输出到控制台
        //输出方式二
        PrintWriter printWriter = new PrintWriter(new FileWriter("printWriter.txt"));//输出到文件中
        printWriter.write("你好,世界");
        printWriter.close();//一定要关闭,否则不写入数据
    }
}
1.4.9Properties

1.4.10练习
public class exer1 {
    public static void main(String[] args) throws Exception{
        File file = new File("D:\\desktop\\mytemp");
        if(!file.exists()){
            file.mkdirs();
        }
        File file1 = new File("D:\\desktop\\mytemp\\hello.txt");
        if(file1.exists()){
            System.out.println("该文件已存在!");
        }else{
            file1.createNewFile();
        }
        FileOutputStream fos= new FileOutputStream("D:\\desktop\\mytemp\\hello.txt");
        fos.write("hello wolrd".getBytes(StandardCharsets.UTF_8));
        fos.close();
    }
}
public class exer2 {
    public static void main(String[] args) throws Exception{
        String filePath = "story.txt";
        BufferedReader buffer = new BufferedReader(new InputStreamReader(new FileInputStream(filePath),"utf-8"));
        String context = null;
        int lineCount = 1;
        while((context = buffer.readLine()) != null){
            System.out.println((lineCount++) + context);
        }
        buffer.close();
    }
}
public class Dog implements Serializable {
    private String name;
    private int age;
    private String color;
    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 String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
    public Dog(String name, int age, String color) {
        this.name = name;
        this.age = age;
        this.color = color;
    }
    public Dog() {
    }
    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", color='" + color + '\'' +
                '}';
    }
}
public class exer3 {
    public static void main(String[] args) throws Exception{
        Properties pros = new Properties();
        pros.load(new FileReader("dog.properties"));
        Dog dog = new Dog(pros.getProperty("name"), Integer.parseInt(pros.getProperty("age")), pros.getProperty("color"));
        System.out.println(dog);//Dog{name='tom', age=5, color='red'}
        String filePath = "dog.dat";
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
        oos.writeObject(dog);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
        Object o = ois.readObject();
        Dog dog1 = (Dog)o;
        System.out.println(dog1.getClass());//class day07.Exer.Dog
        oos.close();
        ois.close();
    }
}
1.4.11总结
-  File类主要是创建一个内存的虚拟的对象,然后对文件/目录进行操作 
-  IO流就包括两大类输入流和输出流 
-  输入流和输出流又分字节和字符输入输出 
-  按照功能分,又分为节点流和处理流,处理流功能更强 
-  总之,所有的流都与最初的File流息息相关 
Day08 多线程
1.多线程
1.1概念
-  程序是静态的概念,进程是运行的程序 
-  线程由进程创建,一个进程由多个线程组成 
-  单线程:同一时间只能运行一个线程;多线程:同一时间可以运行多个线程 
-  **并发:**同一时刻,任务交替进行,貌似“同时执行”(人有很多行为都是并发的),单核 **并行:**同一时刻,多个任务同时执行,多核 并发和并行可以同时存在 





1.2线程的使用
- 线程可以继承Tread类,实现Runnable接口,实现Collable接口,还有线程池四种方式获取


1.2.1多线程机制
-  当开启一个进程的时候,main线程同时开启,此时再开一个线程会交替执行。如果是多核,那么可能并行,单核的话,并发 
-  开启一个进程,main线程开启,Thread - 0由main线程开启,main线程结束,子线程不一定结束,当所有的线程结束之后 进程才结束 


1.2.2start方法
- run方法就是一个普通的方法,start方法才能真正开一个线程
- start方法,调用start0方法开启新线程

1.2.3实现Runnable接口


1.2.4线程方法





1.3线程生命周期


1.4线程的同步⭐️⭐️⭐️
- 出现问题:多个线程对同一份数据进行修改操作的时候
- 设计:将线程要操作共享数据的部分设置为同步代码块
- 总归:在操作共享数据时,只能有一个线程在操作⭐️⭐️⭐️




1.4.1解决方法(Synchronized代码块和方法)
- 锁必须共用一把,即同一个对象
- 锁充当,可以自定义对象,当前对象,类对象(.class)
- 类对象是唯一的,类只会加载一次
- 同步代码一定要是要共享操作的代码,既不能同步多,也不能同步少
- 同步方法适用于一个方法都是对共享的数据进行操作,同步方法默认的是this锁



/**
 * 使用同步机制将单例模式中的懒汉式改写为线程安全的
 *
 * @author shkstart
 * @create 2019-02-15 下午 2:50
 */
public class BankTest {
}
class Bank{
    private Bank(){}
    private static Bank instance = null;
    public static Bank getInstance(){
        //方式一:效率稍差
//        synchronized (Bank.class) {
//            if(instance == null){
//
//                instance = new Bank();
//            }
//            return instance;
//        }
        //方式二:效率更高
        //双重校验锁
        if(instance == null){
            synchronized (Bank.class) {
                if(instance == null){
                    instance = new Bank();
                }
            }
        }
        return instance;
    }
}
1.4.2死锁问题

/**
 * 演示线程的死锁问题
 *
 * 1.死锁的理解:不同的线程分别占用对方需要的同步资源不放弃,
 * 都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
 *
 * 2.说明:
 * 1)出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
 * 2)我们使用同步时,要避免出现死锁。
 *
 * @author shkstart
 * @create 2019-02-15 下午 3:20
 */
public class ThreadTest {
    public static void main(String[] args) {
        StringBuffer s1 = new StringBuffer();
        StringBuffer s2 = new StringBuffer();
        new Thread(){
            @Override
            public void run() {
                synchronized (s1){
                    s1.append("a");
                    s2.append("1");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (s2){
                        s1.append("b");
                        s2.append("2");
                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }.start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (s2){
                    s1.append("c");
                    s2.append("3");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (s1){
                        s1.append("d");
                        s2.append("4");
                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }).start();
    }
}
1.4.3Lock锁(线程安全)
- 构造器有参数fair,参数fair为true为公平的,先进先出,比如三个线程轮流强,不会一个线程多次抢到
- 对象方法lock()为锁住,unlock()为释放锁




class Window implements Runnable{
    private int ticket = 100;
    //1.实例化ReentrantLock
    //参数fair为true为公平的,先进先出,比如三个线程轮流强,不会一个线程多次抢到
    private ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while(true){
            try{
                //2.调用锁定方法lock()
                lock.lock();
                if(ticket > 0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":售票,票号为:" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }finally {
                //3.调用解锁方法:unlock()
                lock.unlock();
            }
        }
    }
}
public class LockTest {
    public static void main(String[] args) {
        Window w = new Window();
        Thread t1 = new Thread(w);
        Thread t2 = new Thread(w);
        Thread t3 = new Thread(w);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}
1.5线程通信
-  线程通信一定要在同步代码块中 
-  sleep不会释放锁,wait会释放锁,并且阻塞 
-  wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。 
-  notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。 
-  notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。 
-  三个通信方法的调用,必须是同步代码块或同步方法中的锁,是同一把锁才行 
-  阻塞状态:就是当前线程不再执行  


/**
 * 线程通信的应用:经典例题:生产者/消费者问题
 *
 * 生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,
 * 店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员
 * 会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品
 * 了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。
 *
 * 分析:
 * 1. 是否是多线程问题?是,生产者线程,消费者线程
 * 2. 是否有共享数据?是,店员(或产品)
 * 3. 如何解决线程的安全问题?同步机制,有三种方法
 * 4. 是否涉及线程的通信?是
 *
 * @author shkstart
 * @create 2019-02-15 下午 4:48
 */
class Clerk{
    private int productCount = 0;
    //生产产品
    public synchronized void produceProduct() {
        if(productCount < 20){
            productCount++;
            System.out.println(Thread.currentThread().getName() + ":开始生产第" + productCount + "个产品");
            notify();
        }else{
            //等待
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    //消费产品
    public synchronized void consumeProduct() {
        if(productCount > 0){
            System.out.println(Thread.currentThread().getName() + ":开始消费第" + productCount + "个产品");
            productCount--;
            notify();
        }else{
            //等待
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Producer extends Thread{//生产者
    private Clerk clerk;
    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }
    @Override
    public void run() {
        System.out.println(getName() + ":开始生产产品.....");
        while(true){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.produceProduct();
        }
    }
}
class Consumer extends Thread{//消费者
    private Clerk clerk;
    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }
    @Override
    public void run() {
        System.out.println(getName() + ":开始消费产品.....");
        while(true){
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.consumeProduct();
        }
    }
}
public class ProductTest {
    public static void main(String[] args) {
        Clerk clerk = new Clerk();
        Producer p1 = new Producer(clerk);
        p1.setName("生产者1");
        Consumer c1 = new Consumer(clerk);
        c1.setName("消费者1");
        Consumer c2 = new Consumer(clerk);
        c2.setName("消费者2");
        p1.start();
        c1.start();
        c2.start();
    }
}
1.6新增线程创建方法(jdk5.0~)
-  方式一步骤: //1.创建一个实现Callable的实现类 //2.实现call方法,将此线程需要执行的操作声明在call()中 //3.创建Callable接口实现类的对象 //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象 //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start() //6.需要获取返回值,用FutureTask的对象的get方法,不需要返回值,实现call方法返回null 




-  方式二步骤: //1.提供指定线程数量的线程池 //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象 //3.3.关闭连接池 


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
 * 创建线程的方式四:使用线程池
 *
 * 好处:
 * 1.提高响应速度(减少了创建新线程的时间)
 * 2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
 * 3.便于线程管理
 *      corePoolSize:核心池的大小
 *      maximumPoolSize:最大线程数
 *      keepAliveTime:线程没有任务时最多保持多长时间后会终止
 *
 *
 * 面试题:创建多线程有几种方式?四种!
 * @author shkstart
 * @create 2019-02-15 下午 6:30
 */
class NumberThread implements Runnable{
    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}
class NumberThread1 implements Runnable{
    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}
public class ThreadPool {
    public static void main(String[] args) {
        //1. 提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        //设置线程池的属性
//        System.out.println(service.getClass());
//        service1.setCorePoolSize(15);
//        service1.setKeepAliveTime();
        //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
        service.execute(new NumberThread());//适合适用于Runnable
        service.execute(new NumberThread1());//适合适用于Runnable
//        service.submit(Callable callable);//适合使用于Callable
        //3.关闭连接池
        service.shutdown();
    }
}
Day09 网络编程
1.网络编程
1.1基础概念



1.1.2ip地址
- ipv4是由4个字节进行表示,一共32位,用十进制表示
- ipv6是由16个字节进行表示,一共128位,一般用十六进制表示




- ipv4地址分类

1.1.3域名和端口号


1.1.4网络协议

-  通过协议,使数据准确无误地传到用户手中  
-  网络数据传输图 


1.1.5TCP和UDP⭐️⭐️⭐️


1.2InetAddress类
-  InetAddress表示IP,和File类获取形式很像 
-  InetAddress两个关键属性 主机名 / IP地址 
-  方法一类是获取主机对象 一类是输出主机名字和地址 
-  获取本机对象getLocalHost(静态方法) 
-  获取指定域名/IP对象getByName(静态方法) 
-  获取对象的主机名getHostName 
-  获取对象的地址getHostAddress  


1.3Socket
-  Socket相当于数据两端的插头 
-  端口号与IP地址的组合得出一个网络套接字:Socket 
-  TCP和UDP编程都要用到socket 
-  socket用完要及时关闭,否则连接数过多会浪费资源 
-  客户端和服务端各有一个socket对象  




1.4TCP编程⭐️⭐️⭐️
-  每次进行TCP网络编程的时候,都要获取Socket对象(IP(InetAddress) + 端口号)     
1.4.1字节流
-  每次发送完数据后,应该有一个结束标记 
-  read是个阻塞式方法,没有读到结束符就不会停止,客户端write方法没有设置停止符号,所以会堵塞。 在IO中,以文件形式传输会自动加停止符 在网络编程中,socket中outputStream会将数据输出到Socket管道中,不会自动加停止符,所以会堵塞 


public class SocketTCP01Client {
    public static void main(String[] args) throws Exception{
        //思路
        //1. 连接服务端 (ip , 端口)
        //解读: 连接本机的 9999 端口, 如果连接成功,返回 Socket 对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        System.out.println("客户端发起连接");
        //2. 连接上后,生成 Socket, 通过 socket.getOutputStream()
        OutputStream os = socket.getOutputStream();
        //3. 通过输出流,写入数据到 数据通道
        os.write("hello server".getBytes(StandardCharsets.UTF_8));
        //4. 关闭流对象和 socket, 
        os.close();
        socket.close();
    }
}
public class SocketTCP01Server {
    public static void main(String[] args) throws Exception{
        //思路 一定要注意这里new的ServerSocket对象,用来建立多个socket连接
        //1. 在本机 的 9999 端口监听, 等待连接
        // 细节: 要求在本机没有其它服务在监听 9999
        // 细节:这个 ServerSocket 可以通过 accept() 返回多个 Socket[多个]
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务器端等待连接.....");
        //2. 当没有客户端连接 9999 端口时,程序会 阻塞, 等待连接
        // 如果有客户端连接,则会返回 Socket 对象,程序继续
        Socket socket = serverSocket.accept();
        //3. 通过 socket.getInputStream() 读取客户端写入到数据通道的数据, 显示
        InputStream is = socket.getInputStream();
        byte[] buf = new byte[1024];
        int length;
        while((length = is.read(buf)) != -1){
            System.out.println(new String(buf,0,length));
        }
        //5.关闭流和 socket
        is.close();
        socket.close();
        serverSocket.close();
    }
}


public class SocketTCP02Server {
    public static void main(String[] args) throws Exception{
        //思路 一定要注意这里new的ServerSocket对象,用来建立多个socket连接
        //1. 在本机 的 9999 端口监听, 等待连接
        // 细节: 要求在本机没有其它服务在监听 9999
        // 细节:这个 ServerSocket 可以通过 accept() 返回多个 Socket[多个]
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务器端等待连接.....");
        //2. 当没有客户端连接 9999 端口时,程序会 阻塞, 等待连接
        // 如果有客户端连接,则会返回 Socket 对象,程序继续
        Socket socket = serverSocket.accept();
        //3. 通过 socket.getInputStream() 读取客户端写入到数据通道的数据, 显示
        InputStream is = socket.getInputStream();
        byte[] buf = new byte[1024];
        int length;
        while((length = is.read(buf)) != -1){
            System.out.println(new String(buf,0,length));
        }
        //4.向服务端写入数据
        OutputStream os = socket.getOutputStream();
        os.write("hello client".getBytes(StandardCharsets.UTF_8));
        //5. 设置结束标记
        socket.shutdownOutput();
        //6.关闭流和 socket
        is.close();
        os.close();
        socket.close();
        serverSocket.close();
    }
}
public class SocketTCP02Client {
    public static void main(String[] args) throws Exception{
        //思路
        //1. 连接服务端 (ip , 端口)
        //解读: 连接本机的 9999 端口, 如果连接成功,返回 Socket 对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        System.out.println("客户端发起连接");
        //2. 连接上后,生成 Socket, 通过 socket.getOutputStream()
        OutputStream os = socket.getOutputStream();
        //3. 通过输出流,写入数据到 数据通道
        os.write("hello server".getBytes(StandardCharsets.UTF_8));
        socket.shutdownOutput();
        //4.获取从服务器端得到的数据
        InputStream is = socket.getInputStream();
        byte[] buff = new byte[1024];
        int length = 0;
        while((length = is.read(buff)) != -1){
            System.out.println(new String(buff,0,length));
        }
        //5. 关闭流对象和 socket,
        is.close();
        os.close();
        socket.close();
    }
}
1.4.2字符流
- 如果使用字符流,一定要刷新,否则不会写入数据通道⭐️因为输入输出字符流,只有在关闭的时候才会写入数据
- 如果用BufferedWriter有一个newline插入一个换行符作为结束符,相应的读取要用BufferedReader的readline方法读取

public class SocketTCP03Server {
    public static void main(String[] args) throws Exception{
        //思路 一定要注意这里new的ServerSocket对象,用来建立多个socket连接
        //1. 在本机 的 9999 端口监听, 等待连接
        // 细节: 要求在本机没有其它服务在监听 9999
        // 细节:这个 ServerSocket 可以通过 accept() 返回多个 Socket[多个]
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务器端等待连接.....");
        //2. 当没有客户端连接 9999 端口时,程序会 阻塞, 等待连接
        // 如果有客户端连接,则会返回 Socket 对象,程序继续
        Socket socket = serverSocket.accept();
        //3. 通过 socket.getInputStream() 读取客户端写入到数据通道的数据, 显示
        BufferedReader bf = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));
        String line = null;
        //下面这种写法是不对的,因为只插入了一个换行符
//        while((line = bf.readLine()) != null){
//            System.out.println(line);
//        }
        String s = bf.readLine();
        System.out.println(s);
        //4.向客户端写入数据
        BufferedWriter br = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "utf-8"));
        br.write("hello client 字符流");
        br.newLine();//插入换行结束符
        br.flush();一定要刷新
        //6.关闭流和 socket
        bf.close();
        br.close();
        socket.close();
        serverSocket.close();
    }
}
@SuppressWarnings("all")
public class SocketTCP03Client {
    public static void main(String[] args) throws Exception{
        //思路
        //1. 连接服务端 (ip , 端口)
        //解读: 连接本机的 9999 端口, 如果连接成功,返回 Socket 对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        System.out.println("客户端发起连接");
        //2. 连接上后,生成 Socket, 通过 socket.getOutputStream()
        OutputStream os = socket.getOutputStream();
        //3. 通过输出流,写入数据到 数据通道
        OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
        BufferedWriter bW = new BufferedWriter(osw);
        bW.write("hello server 字符流");
        bW.newLine();//插入换行符,此时要求对方要用readline()读取
        bW.flush();//一定要刷新,否则不会进通道
//        socket.shutdownOutput();
        //4.获取从服务器端得到的数据
        BufferedReader bf = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));
        String line = null;
        //这样不行
//        while((line = bf.readLine()) != null){
//            System.out.println(line);
//        }
        String s = bf.readLine();
        System.out.println(s);
        //5. 关闭流对象和 socket,
        bW.close();
        bf.close();
        socket.close();
    }
}
1.5UDP网络编程
- UDP先启动哪个端没有问题,只管发送数据就行
- TCP必须先启动服务端,因为如果先启动客户端,客户端就去握手,发现无法握手就抛出异常





/**
 * UDPd协议的网络编程
 * @author shkstart
 * @create 2019 下午 4:34
 */
public class UDPTest {
    //发送端
    @Test
    public void sender() throws IOException {
        //这个是空参数的,send方法发送数据包
        //有参数的是接收端,指定在哪个接口接收
        DatagramSocket socket = new DatagramSocket();
        String str = "我是UDP方式发送的导弹";
        byte[] data = str.getBytes();
        InetAddress inet = InetAddress.getLocalHost();
        //数据包要指定内容,也要指定发送的位置
        DatagramPacket packet = new DatagramPacket(data,0,data.length,inet,9090);
        //
        socket.send(packet);
        socket.close();
    }
    //接收端
    @Test
    public void receiver() throws IOException {
        DatagramSocket socket = new DatagramSocket(9090);
        byte[] buffer = new byte[100];
        DatagramPacket packet = new DatagramPacket(buffer,0,buffer.length);
        socket.receive(packet);
        //packet.getLength()是看写进去几个字节的数据
        System.out.println(new String(packet.getData(),0,packet.getLength()));
        socket.close();
    }
}
1.6URL编程


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fzaKOFHI-1681997063947)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202304181615299.png)]


public class URLTest1 {
    public static void main(String[] args) {
        HttpURLConnection urlConnection = null;
        InputStream is = null;
        FileOutputStream fos = null;
        try {
            //1.获取URL类对象
            URL url = new URL("http://localhost:8080/examples/beauty.jpg");
			
            //2.创建相应的连接对象
            urlConnection = (HttpURLConnection) url.openConnection();
			
            //进行连接
            urlConnection.connect();
			
            //获取数据
            is = urlConnection.getInputStream();
            fos = new FileOutputStream("day10\\beauty3.jpg");
            byte[] buffer = new byte[1024];
            int len;
            while((len = is.read(buffer)) != -1){
                fos.write(buffer,0,len);
            }
            System.out.println("下载完成");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            if(is != null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(urlConnection != null){
                urlConnection.disconnect();
            }
        }
    }
}


Day10 反射
1.反射
1.1反射概念和机制
1.1.1举例






1.1.2理解
- 虚拟机加载完类之后,会在堆中自动创建一个类对象(只有一个),这个类对象包含了类所有的信息

1.1.2.1反射原理图⭐️⭐️⭐️

1.1.2.2反射功能

1.2反射相关类
- 类、构造器、属性、方法

1.3反射的优点和缺点


1.4class类



1.4.1class类方法
- 在输入类的加载路径时,路径是"包名.包名.类型"的格式

1.4.2获取class类对象的六种方式



public class getClass_ {
    public static void main(String[] args) throws Exception{
        //1.Class.forName()方法获取类
        //适用于读取配置文件
        String filePath = "day10.reflection.Car";
        Class<?> cls1 = Class.forName(filePath);
        System.out.println(cls1);
        //2.类名.class
        //适用于参数传递,如获取构造类的时,指定参数
        Class<Car> cls2 = Car.class;
        System.out.println(cls2);
        //3.对象.getClass()
        //适用于有对象实例时
        Car car = new Car();
        Class<? extends Car> cls3 = car.getClass();
        System.out.println(cls3);
        //4.通过类加载器来获取类的Class对象
        //一个类的类加载器是特殊的,加载一个特定的类
        //(1)得到类的加载器
        ClassLoader classLoader = car.getClass().getClassLoader();
        //(2)通过类加载器加载Class对象
        Class<?> cls4 = classLoader.loadClass("day10.reflection.Car");
        System.out.println(cls4);
        System.out.println(cls1.hashCode());//25126016
        System.out.println(cls2.hashCode());//25126016
        System.out.println(cls3.hashCode());//25126016
        System.out.println(cls4.hashCode());//25126016
        //5.基本数据类型获取Class类对象
        Class<Integer> cls5 = int.class;
        System.out.println(cls5);//int
        //6.基本数据类型的包装类通过.TYPE获取Class对象
        Class<Integer> type = Integer.TYPE;
        System.out.println(type);//int
        System.out.println(cls5 == type);//true int.class和Integer.TYPE得到的是一样的类型
    }
}

public class AllTypeClass {
    public static void main(String[] args) {
        Class<String> cls1 = String.class;//外部类
        Class<Serializable> cls2 = Serializable.class;//接口
        Class<Integer[]> cls3 = Integer[].class;//数组
        Class<float[][]> cls4 = float[][].class;//二维数组
        Class<Deprecated> cls5 = Deprecated.class;//注解
                                                    
        Class<Thread.State> cls6 = Thread.State.class;//枚举
        Class<Long> cls7 = long.class;//基本数据类型
        Class<Void> cls8 = void.class;//void 数据类型
        Class<Class> cls9 = Class.class;//
        System.out.println(cls1);
        System.out.println(cls2);
        System.out.println(cls3);
        System.out.println(cls4);
        System.out.println(cls5);
        System.out.println(cls6);
        System.out.println(cls7);
        System.out.println(cls8);
        System.out.println(cls9);
    }
}
1.5类加载
1.5.1类加载时机⭐️⭐️⭐️
-  静态加载和动态加载:⭐️⭐️⭐️ 静态加载:直接new对象的方式属于静态加载,即编译的时候会把new的对象的类进行加载 静态加载直接在编译的时候就已经把类信息存在方法区中,这样运行时省去加载类再创建对象的时间,运行更快 动态加载(延迟加载):反射就属于动态加载,如Class.forName(),这里只有在运行的时候才执行类加载机制,所以这时候类出问题能通过编译。 






1.5.2类加载各个阶段


1.5.2.1加载阶段
- 将二进制数据放入方法区,并且在堆中创建一个相应的Class对象

1.5.2.2连接
1.5.2.2.1验证阶段


1.5.2.2.2准备


1.5.2.2.3解析

1.5.2.3初始化

1.6通过反射获取类的结构信息
1.6.1第一组
-  第一组API需要注意的是: 非Declared方法,能获取子类和子类继承父类所有的public方法,不能获取protected和private,但是构造器只能获取本类的public构造器 Declared方法,能获取本类的所有方法 

1.6.2第二组(Field)
- getModifiers()以int方式返回操作符
- getType返回当前属性所属真正类的Class对象,比如name属于String,该field调用此方法就返回String.class
- 这里getName()返回的是Field对象,getType返回的是Field实际对应的对象

1.6.3第三组(Method)
- 这里无非是方法参数名字,返回值名字

1.6.4第四组(Constructor)

@Test
    public void api_02() throws ClassNotFoundException, NoSuchMethodException {
    //得到 Class 对象
        Class<?> personCls = Class.forName("com.hspedu.reflection.Person");
    //getDeclaredFields:获取本类中所有属性
    //规定 说明: 默认修饰符 是 0 , public 是 1 ,private 是 2 ,protected 是 4 , static 是 8 ,final 是 16
        Field[] declaredFields = personCls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本类中所有属性=" + declaredField.getName()
                            + " 该属性的修饰符值=" + declaredField.getModifiers()
                            + " 该属性的类型=" + declaredField.getType());
        }
    //getDeclaredMethods:获取本类中所有方法
        Method[] declaredMethods = personCls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("本类中所有方法=" + declaredMethod.getName()
                    + " 该方法的访问修饰符值=" + declaredMethod.getModifiers()
                    + " 该方法返回类型" + declaredMethod.getReturnType());
    //输出当前这个方法的形参数组情况
            Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println("该方法的形参类型=" + parameterType);
            }
        }
    //getDeclaredConstructors:获取本类中所有构造器
        Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("====================");
            System.out.println("本类中所有构造器=" + declaredConstructor.getName());//这里老师只是输出名
            Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println("该构造器的形参类型=" + parameterType);
            }
        }
    }
1.7运用反射机制进行操作
1.7.1通过反射创造对象
-  之前是创造一个类对象,这里是通过类对象创造一个真正地对象实例 
-  就两种创建方式: ①直接类对象.new instance()无参构造 ②通过类对象获取构造器,构造器再调用new instance(参数)进行创建,有参构造  


1.7.2通过反射操作属性
-  再通过Field对象访问特定实例对象的静态属性时,方法对象形参部分设置为null即可 传入对象也可以,因为静态类的成员变量属于所有类对象 


1.7.3通过反射调用方法
- invoke要返回的对象是Object


1.8动态代理⭐️⭐️⭐️



1.8.1实现步骤⭐️
-  这里的代理类并不是一个implement各个接口的类,而是根据被代理类加载器和被代理类实现接口,动态代理的类 通过Proxy的静态方法,获取一个代理其他类的实际的代理对象 InvocationHandler实现类只是实现代理类要实现的方法 
-  InvocationHandler实现类,其实是 
-  动态代理其实就是创建动态代理工厂的过程: - 创建一个类工厂,提供相应的生成代理类的静态方法,该静态方法中调用Proxy.newProxyInstance(被代理类加载器,被代理类实现接口,方法实现操作类实例);
- 创建一个InvocationHandler实现类,重写invoke方法,该方法用来表示代理类完成的动作+调用被代理类相应方法
 
1.8.1-1

1.8.2-2

1.8.3-3

1.8.4-4

1.8.5-5.main方法实现

public class MyProxyTest {
    public static void main(String[] args) {
        ClothFactory NikeProxy = (ClothFactory) MyProxyFactory.getInstance(new NikeClothFactory());
        NikeProxy.produceCloth();
//        代理类进行一些工作
//        Nike工厂生产一批运动服
        Human SuperManProxy = (Human) MyProxyFactory.getInstance(new SuperMan());
        System.out.println(SuperManProxy.getBelief());
//        代理类进行一些工作
//        I believe I can fly!
    }
}
//1.
class MyProxyFactory{
    public static Object getInstance(Object obj){
        MyHandler handler = new MyHandler(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }
}
//2.
class MyHandler implements InvocationHandler {
    private Object obj;
    public MyHandler(Object obj){
        this.obj = obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理类进行一些工作");
        Object returnValue = method.invoke(obj, args);
        return returnValue;//这一定返回被代理类方法返回值
    }
}
interface ClothFactory{
    void produceCloth();
}
//被代理类
class NikeClothFactory implements ClothFactory{
    @Override
    public void produceCloth() {
        System.out.println("Nike工厂生产一批运动服");
    }
}
interface Human{
    String getBelief();
    void eat(String food);
}
//被代理类
class SuperMan implements Human{
    @Override
    public String getBelief() {
        return "I believe I can fly!";
    }
    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃" + food);
    }
}
1.8.2动态代理与AOP(面向切面编程)



interface Human{
    String getBelief();
    void eat(String food);
}
//被代理类
class SuperMan implements Human{
    @Override
    public String getBelief() {
        return "I believe I can fly!";
    }
    @Override
    public void eat(String food) {
        System.out.println("我喜欢吃" + food);
    }
}
class HumanUtil{
    public void method1(){
        System.out.println("====================通用方法一====================");
    }
    public void method2(){
        System.out.println("====================通用方法二====================");
    }
}
/*
要想实现动态代理,需要解决的问题?
问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。
 */
class ProxyFactory{
    //调用此方法,返回一个代理类的对象。解决问题一
    public static Object getProxyInstance(Object obj){//obj:被代理类的对象
        MyInvocationHandler handler = new MyInvocationHandler();
        handler.bind(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }
}
class MyInvocationHandler implements InvocationHandler{
    private Object obj;//需要使用被代理类的对象进行赋值
    public void bind(Object obj){
        this.obj = obj;
    }
    //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
    //将被代理类要执行的方法a的功能就声明在invoke()中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        HumanUtil util = new HumanUtil();
        util.method1();
        //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
        //obj:被代理类的对象
        Object returnValue = method.invoke(obj,args);
        util.method2();
        //上述方法的返回值就作为当前类中的invoke()的返回值。
        return returnValue;
    }
}
public class ProxyTest {
    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        //proxyInstance:代理类的对象
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
        //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
        String belief = proxyInstance.getBelief();
        System.out.println(belief);
        proxyInstance.eat("四川麻辣烫");
        System.out.println("*****************************");
        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
        proxyClothFactory.produceCloth();
    }
}



















