JavaSE高级篇——IO流,线程,注解以及反射

860 阅读22分钟

Java高级

1、输入输出相关(I/O相关)

  • 流:数据流,在java.io包下
  • 流的分类:文件流,字符流,数据流,对象流,网络流等
  • 文件:一种电脑的存储形式,txt,jpg,doc,ppt,mp4,rar,xls,mp3
  • 文件夹:目录(路径),File-->与电脑上的文件或文件夹产生对应的映射关系
  • File类:File是在内存中的一个对象,与硬盘上的文件和文件夹有映射关系
import java.io;
public class Test {
    public static void main(String[] args){
        //file是文件的映射,是堆内存中创建出来的一个对象空间
        File file = new File(pathname);
        //路径是看创建对象,是否能与硬盘上文件创建映射关系
        //通过文件流去读取文件的内容
        //硬盘上文件名字不区分大小写,内存中File对象,变量名区分大小写
        //文件的一些属性:
        boolean r = file.canRead();//是否可读
        boolean w = file.canWrite();//是否可写
        boolean h = file.isHidden();//文件是否隐藏
        boolean f = file.isFile();//判断是否是一个文件
        boolean d = file.isDirectory();//判断是否是一个文件夹
        boolean e = file.isExcute();//是否可执行
        long size = file.length();//查看文件大小,返回字节数
        Date modifiedDate = file.lastModified();//文件最后修改时间
        Date date = file.setLastModified(time);//修改文件时间
        //以下方法常用
        String a_path = file.getAbsolutePath();//获取文件的绝对路径
        String name = file.getName();//获取文件的名字
        String r_path = //相对路径,没有盘符的写法,去当前工程所在文件夹中寻找,在src文件夹中寻找
        boolean file.crateNewFile();//创建新的文件
        boolean file.mkdir();//创建文件夹
        boolean file.mkdirs();//创建文件夹,外层没有会创建新的文件夹
        String getParent();//获取当前file的父亲file的名字
        File getParentFile();//获取当前的file的父亲的file对象
        String[] list();//获取当前file的所有儿子的名字
        File[] listFiles();//获取所有儿子对象
    }
}


  • 使用示例:
File file = new File("pathname");
boolean bool = file.mkdir();//前提是外层(父元素)真实存在
try {
    boolean value = file.createNewFile();
}catch(IOException e){
    e.printStackTrace();
}
//如果file.list()不为空,说明file是个文件夹,为空说明file是个文件
  • 文件夹的删除:
public class Test {
    public static void main (String[] args){
        Test t = new Test();
        File f = new File("D://myFolder");
        t.deleteFiles(t);
    }
    //使用递归方式删除所有文件
    public void deleteFiles (File file){
        File[] files = file.listFiles();
        //如果files是个文件则为空,为空文件夹length为0
        if(files != null && files.length != 0){
            for(File f:files){
                deleteFiles(f);
            }
        }
        //删除文件
        file.delete();
    }
}
  • 字节型文件流:

    为什么将数据存储在文件中:变量,数组,对象都是存储在内存中,程序执行完毕后,空间会回收

    文件存储在硬盘上是永久性保存的,文件不在内存中,需要通过IO进行更改

    字节型文件输入流:

//字节型文件流(1字节)FileInputStream/FileOutputStream
//字符型文件流(2字节)FileReader/FileWriter
//JDBC:java database connection
//字节型文件输入流,java.io包 继承InputStream类
//构造方法传参:String,File
try {
      File file = new File("D://myFolder/yyy/1.txt");
      FileInputStream files = new FileInputStream(file);
      int i = files.read();//读取一个字节,读不到返回值-1
      //一次读取一个
      while(i!=-1){
          System.out.print((char)i);
          i = files.read();
         }
      }catch(IOException e){
            System.out.println("catch an error.");
            e.printStackTrace();
      }catch(Exception e){
            e.printStackTrace();
}}
//改进让读取更快
/*txt文件:
hello world,(默认有\t制表符)
nihao
China
*/
//健壮性的写法
FileInputStream files = null;
try {
    File file = new File("D://myFolders/yyy/1.txt");
    //文件不允许不存在
    files = new FileInputStream(file);
    byte[] b = new byte[5];
    int i = files.read(b);
    while(i!=-1){
        System.out.print(new String(b,0,i));
        i = files.read(b);
        /*输出:
        hello world, 
        nihao
        ChinaChin
        原因:
        1:hello
        2:\sworl
        3:d,\n\tn
        4:ihao\n
        5:\tChin
        6:aChin
        */
    }catch(Exception e){
        e.printStackTrace();
    }finally {
        try{
            if(files!=null){
            files.close();
            }
        }catch(Exception e){
            e.printStackTrace();
            }
        }
    }


FileInputStream的方法:

1、int available();

2、int code = read();每次从流管道中读取一个字节,返回字节的code码

3、int count = read(byte[] b);每次从管道中读取个字节存入数组,返回有效元素的个数

4、long skip(long n);跳过n个字节往下读;使用场景,多线程,线程1读取1-2000,线程2读取2001-4000,...然后最后将文件拼接成完整文件

5、close()必须执行,无参数无返回值,关闭流管道,建议将关闭操作放在finally里

我们打开的文件,对文件进行删除操作时允许的,必须关闭流管道后才能删除

文件流的原理图:

(1)字节型文件输出流
  • FileOutputStream:讲数据写入文件中
  • java.io包下,父类是OutputStream
public class Test {
    public static void main (String[] args){
        try{
           File file = new File("D://myFolder/yyy/1.txt");
            //不管文件是否存在,输出流会创建一个文件
           FileOutputStream ofile = new FileOutputStream(file,true);//布尔值表示是否追加,否则每次创建文件覆盖
           ofile.write(97);//写入字符
           ofile.flush();//将管道中字符推到文件中去
        }catch(IOException e){
            e.printStackTrace();
        }finally{
            try{
                if(ofile!=null){
                    ofile.close();
                }
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    }
}


InputStream OutputStream
new FileInputStream(file) new FileOutputStream(file,true)
new FileInputStream("path") new FileOutputStream("path",true)
int read() write(int code)
int read(byte[]) write(byte[],0,count)
close() close()
available()查看管道中有多少字节缓存 flush()将管道中字节型文件流推入文件
skip(long count)跳过多少字节进行读取

实现文件复制代码:

public class OperateFile {
    public void copy(File file,String path){
        try {
           //获取要拷贝的对象
           FileInputStream ifile = new FileInputStream(file);
           byte[] b = new byte[1024];//1kb-8kb比较好
           int count = ifile.read(b);
           //写入的文件
           File tfile = new File(path+"/"+file.getName());
           FileOutputStream ofile = new FileOutputStream(tfile);
           while(count!=null){
               ofile.write(b,0,count);
               ofile.flush();
               count = ifile.read(b);
           }
        }catch (IOException e){
           e.printStackTrace();
        }finally{
            //两个文件分别单独判断然后关闭
            //分开判断是为了防止意外前面的报错,后面的管道也无法关闭
        }
    }
}

文件夹的复制:

public void copyFile (File file,String path){
    //判断file是文件还是文件夹
    File files = file.listFiles();
    String oldPath = file.getAbsolutePath();
    String newPath = path.concat(oldPath.split(":")[1]);
    File newFile = new File(newPath);
    if(files != null){
        //创建新的file对象
        newFile.mkdir();
        if(files.length!=0){
            for(File f:files){
                //此时的新路径就是要拷贝的路径
                copyFile(f,newPath);
            }
        }
    }else{
        //对文件流的操作
        FileInputStream inFile = null;
        FileOutputStream outFile = null;
        try{
            inFile = new FileInputStream(oldPath);
            outFile = new FileOutputStream(newPath);
            byte[] b = new byte[1024];
        	int count = inFile.read(b);
            while(count!=-1){
                outFile.write(b,0,count);
                outFile.flush();
                count = inFile.read(b);
            }
        }catch(IOException e){
            e.printStackTrace();
        }finally{
            try{
                if(inFile!=null){
                inFile.close();
            }catch(IOException e){
                 e.printStackTrace();
              }
            try{
                if(outFile!=null){
                inFile.close();
               }
            }catch(IOException e){
                 e.printStackTrace();
              }
            }
        }
    }

剪切操作:

public void cut (File file,String path){
    this.copy(file,path);
    this.delete(file);
}

(2)字符型文件流

字节流的好处在于可以处理任意格式文件,但是处理中文的时候可能出现字符拼接问题

原因:因为中文由两个字节组成,拼接的时候容易与其它字节拼错,出现乱码

字符型文件输出流的类:FileReader(),FileWriter()

1)在java.io包下,构造方法可以传文件路径或者传文件

2)特点:继承OutputStreamWriter可以读取char[]数组

3)使用:只针对与能用记事本打开的纯文本文件.txt .properties

4)FileWriter(file,true);true表示文件形式为追加,否则均为覆盖

public class Test {
    public static void main (String[] args){
        try{
            File file = new File("D:\\myFolder\\yyy\\y2.txt");
            FileReader fd = new FileReader(file);
            char[] c = new char[20];
            int count = fd.read(c);
            while(count!=-1){
                //一般编码使用GBK
                System.out.print(new String(c,0,count));
                count = fd.read(c);
            }
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

FileWriter:
与FilerReader基本一样,不同的是write方法:
write(int);
write(char[]);
write(String str);//可以写字符串,较为方便
//注意使用close方法关闭

(3)编码字符集的问题:

eclipse默认是GBK的编码字符集

IDEA默认是UTF-8字符集,将记事本的格式改为utf-8就可以

字符:文字和符号的总称;不同国家的数字、符号、字母是一样的

字符编码:将字符进行拆分和组合的规则

编码集:

1)ASCII(American Standard Code for Information Interchange)只支持英文

2)ISO-8859-1不支持中文

3)GB2312 GB18030 GBK BIG5,Windows平台默认字符集GBK,Linux和Mac OS默认字符集是utf-8

4)Unicode

5)UTF-8

6)UTF-16

(4)缓冲流

在流管道中增加缓存的数据,让我们使用流读取的文字更加顺畅,高级流,通过低级流创建,性能更高。

类:BufferedInputStream/BufferedOutputStream/BufferedReader/Bufferedwriter

BufferedInputStream/BufferedOutputStream:

public class Test {
    public static void main (String[] args){
        try{
            File file = new File("D://myFolder/xxx/x1.txt");
            FileInputStream inFile = new FileInputStream(file);
       		BufferedInputStream bis = new BufferedInputStream(inFile);
            //使用方法与低级流相似
            bis.read(int/byte);
            bis.isAvailable();//查看缓存中字符数
            bis.skip(long);
            bis.close();
        }catch(IOException e){
            e.printStackTrace();
        }
    }
}

BufferedReader/BufferedWriter:

//创建字符输入流
File file = new File(path);
FileReader fr = new FileReader(file);
BufferedFileReader bfr = new BufferedFileReader(fr);
int code = bfr.read();
//使用方式与其他流差不多,但是有一个比较方便的方法
String bfr.readLine();//读取文件中一行的信息
String value = bfr.readLine();
while(value!=null){
    System.out.println(value);
    value = bfe.readLine();
}

文件流

低级流:字节型(FileInputStream,FileOutputStream),字符型(FileReader,FileWriter

高级流:字节型(BufferedInputStream,BufferedOutputStream),字符型(BufferedReader,BufferedWriter

对象流:ObjectInputStreamObjectOuputStream序列化

(5)对象流

1)对象的序列化和反序列化

序列化:将一个完整的对象拆分成字节碎片记录在文件中(需要实现Serializable序列化接口)

反序列化:将文件记录的对象反过来组合成一个完整的对象(需要提供版本号),目的是JDK版本不同读出的对象不一致,高级版本可以读低级版本的对象,但是低级版本读高级版本的序列化对象是不允许的

2)文件的作用

将文件永久性保存,使数据持久化

3)按照以行为单位读取/写入信息的优缺点

好处是每一行记录的信息都是相关的,信息直接读取出来,直接了解文件;缺点是不安全,性能差,采用分布式将一个文件拆分成碎片,只能记录String信息,不能记录一些动作和行为

*4)*对象流可以直接将一个对象的属性和行为写进一个对象

public class Person implements Serializable{
    private String name;
    private int age;
    //IDEA 由快捷键生成这个ID
    private long final serialVersionID = 437194743254L;
    public Person (String name,int age){
        this.name = name;
        this.age = age;
    }
   public void talk (){
       System.out.println("hello,I am"+this.name);
   }
}
//EOFException:说明没有对象可以写了,通常会将所有记录的对象存在一个集合里
public class Test {
    public static void main (String[] args){
        try{
        Person p = new Person("chen",20);
        //高级流,需要低级流
        //对象的序列化,必须是先Serializable接口(示意性接口,没有需要重写的方法)
        FileOutputStream file = new FileOutputStream("D://1.txt");
        ObjectOutputStream os = new ObjectOutputStream(file);
        os.writeObject(p);
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try{
                if(os!=null){
                    os.close();
                }
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}

使用inteliJ IDEA如何设置自动生成serialVersionID:

File/Settings/Inspections搜索serialVersion勾选下面两个选项:

  • Serializable class without "serialVersionUID"
  • SerialVersionUID field not declared "private static final"

示意图:

字符串流

StringReaderStringWriter

数据流

DataInputStream,DataOutputStream

转换流

网络相关介绍

打印流

InputStreamReader,PrintWriter

(6)字节型转化为字符型

没错,就是这个流:InputStreamReader

public class Demo {
    public void transferStream () throws Exception {
        
    }
}

小任务:

1.实现一个银行业务系统

2.要求有如下业务功能:登录、查询余额,存款,取款,转账,开户,销户

3.记录银行的用户信息:账号,密码,余额

www.jianshu.com/p/cc3bf9bbd…

MVC分层架构思想:

2、线程相关

线程例子:看视频时,边看视频边调节声音大小

程序:可以理解为一组静态的代码(下载的程序的文件夹里的所有文件)

进程:正在运行的程序,静态的代码执行起来了(点击exe打开软件,放到内存中执行)

线程:进程内的小单元(进程中的最小单元,很多线程同时执行)

主线程:系统线程(JVM虚拟机,必须先执行起来,其它代码才能执行起来)

用户线程:main

守护线程(精灵):GC

线程:操作系统级别CPU

如何在Java中创建线程,让线程执行,多线程

线程的状态图:

如何在Java中实现一个线程:

1.自己描述一个类

2.继承一个父类(Thread)

3.重写run方法

4.new一个线程对象,调用start方法,让线程进入就绪状态,等待cpu分配时间碎片

实现一个跑步的小例子:

public class TestThread {
    //第一种创建线程方法
    Runner r1 = new Runner("jieke");
    Runner r2 = new Runner("fake");
    Runner r3 = new Runner("quene");
    r1.start();
    r2.start();
    r3.start();
    //第二种创建线程的方法:构建成线程对象,然后使用run方法
    Thread a1 = new Thread(r1);
    a1.start();
    Thread a2 = new Thread(r1);
    a2.start();
}

//实现线程的第一个方式
public class Runner extends Thread {
    private String name;
    public Runner (String name){
        this.name = name;
    }
    public run () {
        for(int i = 0;i < 100;i++){
            System.out.println(this.name+" run "+"meters");
        }
    }
}

//实现的第二个方式
public class Runner implements Runnable {
    private String name;
    public Runner (String name){
        this.name = name;
    }
    public run () {
        for(int i = 0;i < 100;i++){
            System.out.println(this.name+" run "+(i+1)+" meters");
        }
    }
}

多线程使用:模拟多个用户同时访问

可能产生问题:

生产消费者模型

特征修饰符:synchronized放在方法的结构上,锁定的是调用方法时的对象

访问对象的线程锁定了它,所以也叫线程锁

synchronized放在方法,构造方法,块的内部

1.第一种;
public synchronized void get (){}
2.第二种:提高性能的;
public void get (){
    //可以并发执行的代码;
    synchronized(对象){
        //不允许线程同步的代码
    }
    //可以并发执行的代码
}
//线程之间状态的切换
this.wait();//是访问当前对象的线程wait

//设置优先级别1-10
production.setPriority(1);
production.getPriority();//获取线程优先级别

IllegalMonitorStateException:告知一个线程等待,结果告知的是另一个线程,会出这个异常

不加锁利用线程等待可能导致的异常:IllegalMonitorStateException

小任务:

微信抢红包例子:5元钱,10个红包,保证每个人至少有0.01

join方法:

Thread类中的方法,可以让并行的线程变成单线程

public class Thread1 extends Thread {
    public void run () {
        System.out.println("Thread one starts");
        Thread2 t2 = new Thread2();
        t2.start();
        try{
            t2.join(2000);//表示让线程先执行2000ms
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("Thread one ends");
    }
}

public class Thread2 extends Thread {
    public void run () {
        System.out.println("Thread two starts");
        try{
            Thread.sleep(3000);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("Thread two ends");
    }
}

public class Test {
    public static void main (String[] args){
        Thread1 t1 = new Thread1();
        t1.start();
        /*执行结果:
        Thread one starts  
        Thread two starts
        Therad one ends
        Thread two ends
        */
    }
}

死锁

哲学家就餐问题:一个方桌四个人,四个筷子,每个人先拿左手边筷子,在拿右手边筷子

程序中可能出现死锁问题

public class Chopstick {
    private int num;
    public Chopstick (int num){
        this.num = num;
    }
    public int getNum (){
        return this.num;
    }
}
public class Person extends Thread{
    public String name;
    public Chopstick left;
    public Chopstick right;
    public Person (String name,Chopstick left,Chopstick right){
        this.name=name;
        this.left=left;
        this.right=right;
    }
    public void run (){
        System.out.println(this.name+"开始吃饭");
        synchronized (left){
            System.out.println(this.name+"拿起了"+left.getNum()+"号筷子");
            synchronized (right){
                System.out.println(this.name+"拿起了"+right.getNum()+"号筷子");
                System.out.println(this.name+"开始吃饭");
            }
        }
    }
}

死锁的解决方法:时间差,不要产生公用对象的问题

计时器/定时器:

public void test (){
    Timer timer = new Timer();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date firstTime = sdf.parse("2019-6-11 15:15:00");
    timer.schedule(new TimerTask(){
        public void run (){
            System.out.println("给XXX发送信息:");
        }
    },firstTime,3000);
}

进程:当前正在运行的程序,一个应用程序在内存中的执行区域。

线程:进程中的一个执行控制单元,执行路径

进程和线程的关系:一个进程可以有一个线程,也可以有多个线程

CPU执行线程的随机性:每个程序中高速切换

多线程提高效率的原理:比如同时复制多个文件,一个进程有多个线程,随机到这个进程的机率就更大,相对而言,效率会更高

多线程实现1:

//线程的创建
public class MyThread extends Thread {
    public void run (){
        for(int i = 0; i < 100; i++){
        System.out.println(this.getName()+":"+i);
        }
    }
}

public class TestThread {
    public static void main (String[] args){
        MyThread mt = new MyThread();
        //给线程设置名字
        mt.setName("张三");
        mt.start();
        MyThread mt2 = new MyThread();
        mt2.setName("老王");
        mt2.start();
    }
}

String getName();//获取线程名字
void setName();//设置线程名字

主方法是单线程的

多线程实现2:

//实现Runnable接口
public class MyThread implements Runnable {
    public void run (){
        for(int i = 0; i < 100; i++){
            //链式编程
        System.out.println(Thread.getCurrentThread().getName()+":"+i);
        }
    }
}

public class Test {
    public static void main (String[] args){
        MyThread mt = new MyThread();
        Thread t = new Thread(mt);
        t.setName("李四");
        t.start();
        
        MyThread mt2 = new MyThread();
        Thread t2 = new Thread(mt2);
        t2.setName("王五");
        t2.start();
    }
}
//获取当前线程
static Thread Thread.getCurrentThread();//获取当前线程

思考:既然已经有Thread父类了,为什么还要一个Runnable接口?

答:因为Java是单继承的,如果继承了一个父类,就不能在继承其它类,所以为了能继承其它类,又能实现多线程,Java提供一个Runnable接口,实际中,推荐使用第二种,实现Runnable接口的方式来进行多线程开发。

利用多线程实现火车站购票的Demo:

public class Ticket implements Runnable {
    private int num;
    public Ticket (int num){
        this.num = num;
    }
    public void run (){
        while(true){
            if(num>0){
              System.out.println(Thread.getCurrentThread().getName()+"卖出"+(this.num--)+"号票"; 
            }else{
                System.out.println("票已经售完");
            }
        }
    }
}
                                 
class Test {
    public static void main (String[] args){
        Ticket t = new Ticket(200);
        Thread thread1 = new Thread(t);
        thread1.setName("window 1");
        Thread thread2 = new Thread(t);
        thread1.setName("window 2");
        Thread thread3 = new Thread(t);
        thread1.setName("window 3");
        thread1.start();
        thread2.start();
        thread3.start();
    } 
}
Thread.sleep();//让程序休眠一会儿

注意:

synchronized:一旦被某个线程访问,其它线程都变成等待状态

1、非静态同步方法锁的对象是this 2、静态的同步的方法锁的对象是当前类的字节码对象

线程的生命周期:

![](C:\Users\86180\Desktop\testGit\typoraImg\线程的生命周期 .png))

3、反射相关

String的内容不是真的不让变,可以通过反射尝试修改value的值

反射:reflect,用来描述所有的类,所有的类也具有相同的特征

反射原理图:

类是用来描述一组对象的,反射机制认为是用来描述一组类的

Class:用来描述类本身

Field:用来描述类的属性

Method:用来描述类中的方法

Constructor:用来描述类中的构造方法

Annotation:用来描述类中的注解

获取类的方式:

Class x = Class.forName("包名.类名");
Class y = 类的实例.class;
对象.getClass();//Object中的方法
常用方法:
1.int getModifiers();//每一个修饰符用一个整数来表示
//0:默认不写 1:public 2:private   4:protected 8:static
//16:final   32:synchronized 64:volatile 128:transsient 256:native
//512:interface 1024:abstract
2.String name = getName();//获取全名,包括包名
3.String simpleName = getSimpleName();//只获取类的名字
4.Package p = getPackage();//然后使用p.getName()获取包的名字
5.Class father = getSuperClass();//获取父类
6.Class[] classes = getInterfaces();//获取类中所有的接口

//获取所有父类的全名
public class Test {
    public static void main (String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Class superClass = ArrayList.class;
        while(superClass.getSuperclass()!=null){
            try{
            System.out.println(superClass.getName()+"的父类是"+superClass.getSuperclass().getName());
            superClass = superClass.getSuperclass();
            }catch(Exception e){
                System.out.println("没有了");
            }
        }
   }
}

//获取所有的接口
public class Test {
	public static void main(String[] args){
		ArrayList<String> list = new ArrayList<>();
        Class superClass = ArrayList.class;
		try {
            //获取class的所有父亲接口
            Class[] classes = superClass.getInterfaces();
            while(classes!=null){
                for(Class c:classes){
                    System.out.println(c);
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
	}
}
7.Object newInstance();//或取无参构造方法的实例
Class clazz = Class.forName("testInterface.Person");
Person p = (Person)clazz.newInstance();//相当于调用person类中的默认无参构造方法
8. Field = getField("属性名");//获取属性,属性必须是公有的
Field[] fields = getFields();//获取类的所有属性
Field getDeclaredField("属性");//获取声明的属性
Field[] getDeclaredFields("属性");//获取所有声明的属性
int getModifiers();
Class getType();//获取属性类型对应的那个class
String getName();
set(Object,"value");//给属性赋值得方法
nameField.get(Object)
 
10. Class[] getClasses();//获取类中的内部类

体会反射的强大:

public class Test {
    public static void main (String[] args) {
        try{
            Class car = Class.forName("velometer.Car");
            Car car1 = (Car)car.newInstance();
            Field speed = car.getDeclaredField("speed");
            //让私有属性可以修改
            speed.setAccessible(true);
            //将具体的对象中的属性进行设置
            speed.set(car1,1000);
            //输出证明私有属性也被我们更改了
            System.out.println(car1.getSpeed());//1000
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}
//虽然是私有的静态属性,只能在本类中设计方法更改,但是反射可以做到,不能修改final修饰的属性
public class Car {
    private static int speed = 50;
    private static String name = "Landroover";
    public Car () {}
    public int getSpeed () {
        return this.speed;
    }
}

通过反射来让字符串能改变:

String str = new String("abc");
Class clazz = str.getClass();
//根据知道的final修饰的value获取到属性
Field field = clazz.getDeclaredField("value");
//将属性变为可修改类型
field.setAccessible(true);
//获取到数组,然后对内容进行修改
char[] temp = (char[])field.get(str);

观察者设计模式(Observer):也可以称之为发布订阅模式

//实现目标的抽象类
public abstract class Subject {
    //设置一个存储观察者的集合
    private ArrayList<Observer> oSet = new ArrayList<>();
    
    //设置一个敏感方法,用来触发所有观察者的状态更新
    public abstract dosomething ();
    
    //唤醒所有观察者
    public abstract notifyObservers();
    
    //添加观察者
    public void attach (Observer o){
        this.oSet.add(o);
    }
    
    //删除观察者    
    public void detach (int index){
        this.oSet.remove(index);
    }
}

//实现观察者的接口
public interface Observer {
    void doself ();
}

反射的方法使用:

public class Test {
    public static void main (String[] args){
        Class p = Class.forName("packa.Person");
        //通过方法名定位方法,通过方法参数类型对应的Class来找寻
        try{
            //第二个参数是参数类型的类,可以获取类和父类的方法
            Method m = p.getMethod("eat",String.class);
            int mm = m.getModifiers();//获取权限和特征修饰符
            Class mrt = m.getReturnType();//获取返回值数据类型
            String mn = m.getName();//获取方法的名字
            Class[] mpts =  m.getParameterTypes();//获取方法参数列表的类型
            Class[] mets = m.getExceptionTypes();//获取异常类型
            String str = (String)m.invoke(p,"测试参数");
            Method[] ms = m.getMethods();
            //使用私有方法
            m.setAccessible("true");
            m.invoke(p,参数);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}
public class Person {
    public String name;
    public void eat (){
        System.out.println("hello");
    }
    public void eat (String food){
        System.out.println("hello");
    }
}

构造方法:

public class Test {
    public static void main (String[] args){
        try{
            Class clazz = Person.class;
            //获取有参数的构造方法
            Constructor con = clazz.getConstructor(String.class);
            //执行构造方法
            Person p = (Person)con.newInstance("haha");
            System.out.println(p);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}
//除了没有返回值,其它方法与一般方法相同
public class Person {
    private String name;
    public Person (){}
    public Person(String name){this.name=name;}
}

反射的使用:

做底层的封装,根据一个字符串创建出一个真实的对象

设计一个小工具,替代我们创建对象的过程,

//Spring开源框架思想:IOV(inverse of control控制反转),AOP(面向切面编程)

public class Test {
    public static void main (String[] args){
       
    }
}
public class MyObject {
    //设计一个方法,帮我们控制对象的创建 
    public Object getBean(String className){
        Object obj = null;
        Scanner input = new Scanner(System.in);
        try{
            Class clazz = Class.forName(className);
            obj = clazz.newInstance();
            int index = clazz.toString().lastIndexOf(".");
            String classType = clazz.toString().substring(index+1);
            //判断是否为Chacracter类型
            //包装类中Character没有String类型的构造方法
            if(classType.equals("Character")){
                    System.out.println("我也不知道要干什么,反射不熟练");
            }else{
                    Field[] fields = clazz.getDeclaredFields();
                    for(Field f:fields){
                        //要得到方法名,最后变为set+Demo类型
                        System.out.println("请给属性"+f.getName()+"赋值:");
                        //要进行的赋值操作
                        String value = input.nextLine();
                        //获取的属性名变为set形式
                        String attrName = f.getName();
                        //通过处理好的set方法名,找寻set方法
                        String firstUpperLetter = attrName.substring(0,1).toUpperCase();
                        String otherLetter = attrName.substring(1);
                        StringBuilder sb = new StringBuilder("set");
                        sb.append(firstUpperLetter);
                        sb.append(otherLetter);
                        //获取field对应的属性所属的类
                        Class fieldClass = f.getType();
                        //获取参数的类型,获取属性的包装类
                        //假设为Integer,则con为Integer的构造方法
                        Constructor con = fieldClass.getConstructor(String.class);
                        //调用传入String的构造方法
                        Method fieldMethod = clazz.getMethod(sb.toString(),fieldClass);
                        //执行,假如obj为Person类,相当于
                        //con.newInstance(value)相当于new Integer(value);
                        //相当于, o.setName(new Integer(value))
                        fieldMethod.invoke(obj,con.newInstance(value));
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        return obj;
}
}
public class Person {
    private String name;
    private Integer age;
    public Person (){}
    public Person (String name,Integer age){
        this.name = name;
        this.age = age;
    }
}

4、注解相关

注释:为了让人们更容易读懂代码,文档注释/** */

注解的写法:@xxx[一些信息]

注解的用法:

1.类的上面(TYPE) 2.属性上面(FIELD) 3.方法上面(METHOD) 4.构造方法上面(CONSTRUCTOR) 5.参数前面

注解的作用:

1.仅用来当作注释 2.检测作用 3.可以携带一些信息

注解中可以携带信息,信息不能随意写,信息类型只能是如下类型:

1.基本数据类型 2.String类型 3.枚举类型enum 4.数组类型 5.注解类型@

import java.lang.annotation.ElementType.*;
//描述当前注解可以放在什么位置
@Target({FIELD,METHOD,CONSTRUCTOR})

//通过@interface定义一个新的注解类型,写法与接口类似
public @interface MyAnnotation {
//注解方法必须有返回值,返回值类型为规定类型
int test();
//我们用value定义且只有一个方法可以省略方法名
//数组内只有一个元素可以省略大括号
String[] value;
}

public class Test {
//如果方法有多个,方法名和传参都不能省
@MyAnnotation(test=10,value={"ha","he"})
private String name;
}

2.元注解:用来说明注释

@Target({...})描述当前注解可以出现的地方

@Retention描述注解可以存在的作用域(一般为RUNTIME)

@Inherited描述当前注解是否能被子类继承

@Document描述当前注解是否能被文档所记录

public class Test{
    public static void main (String[] args){
        Date date = new Date();
        date.getYear();//会发现getYear有删除线
        /*@Deprecated
        *public long getYear(){}
        **/
    }
    
    //提示
    @Override
    public void eat (){}
    //注释
    @Deprecated
    public void fuck (){}
    //携带信息,文件.propertied .xml 注解
    @SuppressWarnings(String[] {"unused","",""})//尽量不用
    unused 未被使用
    serial 未序列化
    rawtypes 集合没有过定义泛型
    deprecation 使用废弃的方法
    unchecked 出现了泛型问题可以不检测
    all 包含以上所有(不推荐使用)
}

如何利用反射技术解析注解

import java.lang.annotation.ElementType.*;
//描述当前注解可以放在什么位置
@Target({FIELD,METHOD,CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String[] value();
}

public class Person {
    @MyAnnotation("chen")
    private String name;
}

public class Test {
    public static void main (String[] args){
        //解析Person类属性上面的注解信息
        Class clazz = Person.class;
        try{
            Field name = clazz.gerDeclaredField("name");
            //通过field获取上面的注解对象
            MyAnnotation ma = (MyAnnotation)field.getAnnotation(MyAnnotation.class);
           
            //1.正常获取注解的值
            String[] values = ma.value();
            System.out.println(values[0]);//chen
            
            //2.利用反射执行a中的方法
            Annotation ma = field.getAnnotation(MyAnnotation.class)
            //获取注解对象的类
            Class maclazz = ma.getClass();
            Method mamethod = maclazz.getMethod("value");
            //主要是注解对象调用方法
            String[] values = (String[])mamethod.invoke(ma);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

注解在开发中应用场景

public class Person {
    private String name;
    private Integer age;
    private String sex;
    @MyAnnotation("chen","18","male")
    public Person (){}
    public String getName() {return this.name;}
    public Integer getAge() {return this.age;}
    public String getSex() {return this.sex;}
    public void setName(String name) {this.name = name;}
    public void setAge(Integer age) {this.age= age;}
    public void setSex(String sex) {this.sex = sex;}
}
public class MySpring {
    Object obj = null;
        try{
            //1.获取目标对象的类
            Class targetClass = Class.forName(className);
            //2.创建一个目标对象
            Constructor targetCon = targetClass.getConstructor();
            obj = targetCon.newInstance();
            //3.获取注解中的所有信息
            Annotation targetAnno = targetCon.getAnnotation(TargetMsg.class);
            Class annoClass = targetAnno.getClass();
            Method annoMethod = annoClass.getMethod("value");
            String[] values = (String[])annoMethod.invoke(targetAnno);
            //4.获取对象的所有属性,遍历
            Field[] fields = targetClass.getDeclaredFields();
            for(int i = 0;i < values.length; i++){
                //5.获取属性名进行拼接,变为set+Name形式
                fields[i].setAccessible(true);
                String fieldName = fields[i].getName();
                StringBuilder sb = new StringBuilder("set");
                String front = fieldName.substring(0,1);
                String newName = sb.append(front.toUpperCase()).append(fieldName.substring(1)).toString();
                //6.获取属性类型对应的String类构造方法
                Class fieldType = fields[i].getType();
                Constructor fieldCon = fieldType.getConstructor(String.class);
                //7.根据拼接获取方法,记得要将方法参数类型放进去
                Method setMehtod = targetClass.getDeclaredMethod(newName,fieldType);
                //8.调用方法设置属性
                setMehtod.invoke(obj,fieldCon.newInstance(values[i]));
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        return obj;
}

Properties的使用:

在java.util包下

HashTable的子类,是一个map类型的集合

文件的后缀名必须是.properties,文件内容必须是key=value形式;分隔符为=、:或空格

//可以认为是一个高级流
Properties pro = new Properties();
pro.load(new FileReader("src/teststream/Test.properties"));
pro.get(key1);
Enumeration en = pro.propertyNames();
	try{
        Properties pro = new Properties();
        //通过低级流读取文件信息,注意必须是properties格式的文件
        pro.load(new FileReader("src/Test.properties"));
        //get方法获取键名对应的值
        System.out.println((String)pro.get("chenxiang"));
        //迭代器
	//en有两个方法:hasMoreElements() nextElement(),使用与Iterator极其相似
        @SuppressWarnings("unchecked")
        Enumeration en = pro.propertyNames();
        while(en.hasMoreElements()){
           String key = (String)en.nextElement();
           String value = (String)pro.getProperty(key);
           System.out.println("{"+key+":"+value+"}");
         }
        }catch(Exception e){
            e.printStackTrace();
        }