Java 入门 (十二)

编码和解码

编码:从看得懂的变成看不懂的 String 2 byte

解码:从看不懂的变成看得懂的 byte 2 String

编码表

GBK:一个英文用一个字节表示,一个中文用两个字节表视

UTF-8:一个英文用一个字节表示,一个中文用三个字节表示

字符流

字符流 = 字节流 + 编码表

用字节流可以处理文本数据吗?

可以,但是要考虑编码的不同。

  1. 因为在GBK编码中,一个中文表示啷个字符,那么用2的倍数的字节数组就可以正确读取。
  2. 如果要是英文,在GBK编码中,一个英文表示一个字节,那么随意用字节数组长度都可以进行正确读取。
  3. 如果要是中文和英文混到一起进行读取,不能保证正确读取

总结:使用字节流可以处理文本数据,但是需要考虑到编码情况

文件字符输出流FileWriter

用来写入字符文件的便捷类,此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的

构造方法:

FileWriter(File file) ;
//根据给定的 File 对象构造一个 FileWriter 对象。 
FileWriter(File file, boolean append) ;
//根据给定的 File 对象构造一个 FileWriter 对象。 
FileWriter(String fileName) ;
//根据给定的文件名构造一个 FileWriter 对象。 
FileWriter(String fileName, boolean append) ;
//根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("b.txt");
        FileWriter fw1 = new FileWriter(new File("b.txt"));
    }

常见方法:

void write(char[] cbuf) ;
// 写入字符数组。 
abstract  void write(char[] cbuf, int off, int len) ;
// 写入字符数组的某一部分。 
void write(int c) ;
// 写入单个字符。 
void write(String str);
// 写入一个字符串

文件字符输入流FileReader

用来读取字符文件的便捷类,此类的构造方法假定默认字符编码和默认字符缓冲区大小都是适当的。

构造方法

FileReader(File file) ;
//在给定从中读取数据的 File 的情况下创建一个新 FileReader。 
FileReader(String fileName) ;
//在给定从中读取数据的文件名的情况下创建一个新 FileReader。

常见方法

int read() ;
//读取单个字符。 
int read(char[] cbuf) ;
//将字符读入数组。 
abstract  int read(char[] cbuf, int off, int len) ;
//将字符读入数组的某一部分。 
int read(CharBuffer target) ;
//试图将字符读入指定的字符缓冲区。 
boolean ready() ;
//判断是否准备读取此流。
FileReader fr = new FileReader("b.txt");
FileReader fr2 = new FileReader(new File("b.txt"));

字符流拷贝

将一个文本文件从一个位置复制到另一个位置

使用字节流和字符流都可以完成文本的拷贝,并且都没有乱码的现象。

但是如果要是打印在控制台上的话(也就是在读取的过程中进行打印),那么字符流不会乱码,而字节流很可能乱码

拷贝文本的时候使用字节流还是字符流

最好使用字节流。因为字符流还要涉及到编码解码的过程,浪费资源和时间

字符流不能用来拷贝除了文本内容之外的数据

  1. 字符流只能拷贝用记事本打开能看懂的内容
  2. 用记事本打开看不懂的内容就只能用字节流

带缓冲的字符流

缓冲字符输出流

将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入

构造方法

BufferedWriter(Writer out);
BufferedWriter(Writer out,int sz)

常见方法

write(int c);
write(char[] chs);
write(char[] chs ,int offset, int len);
write(String s);
write(String s ,int offset ,int len)
newLine();// 特有方法换行

缓冲的字符输入流

从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。

可以指定缓冲区的大小,或者可以使用默认的大小。大多数情况下,默认值就足够用了。

构造方法

BufferedReader(Reader in);
BufferedReader(Reader in, int sz)

常见方法

read();
read(char[] chs);
String readLine();// 一次都一个文本行,以\r\n或\r或\n作为结束的标志。如果有数据就反汇,如果没有数据就返回·null
public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("d.txt"));

        int read = 0;
        while ((read = br.read()) != -1) {
            System.out.print("[1<-->]" + (char) read);
        }
        br.close();
        br = new BufferedReader(new FileReader("d.txt"));
        char[] ch = new char[1024];
        int len = 0;
        while ((len = br.read(ch)) != -1) {
            System.out.print("[2<-->]" + new String(ch, 0, len));
        }
        br.close();
        String str = "";
        br = new BufferedReader(new FileReader("d.txt"));
        while (br.ready()) {
            str = br.readLine();
            System.out.println("[3<-->]" + str);
        }
        br.close();
        br = new BufferedReader(new FileReader("d.txt"));
        while ((str = br.readLine()) != null) {
            str = br.readLine();
            System.out.println("[4<-->]" + str);
        }
        br.close();
    }

转换流

InputStreamReader

InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。

构造方法

InputStreamReader(InputStream in) ;
//创建一个使用默认字符集的 InputStreamReader。 
InputStreamReader(InputStream in, Charset cs) ;
//创建使用给定字符集的 InputStreamReader。 
InputStreamReader(InputStream in, CharsetDecoder dec) ;
//创建使用给定字符集解码器的 InputStreamReader。 
InputStreamReader(InputStream in, String charsetName); 
//创建使用指定字符集的 InputStreamReader。
public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
//      FileInputStream fis = new FileInputStream("a.txt");
//      InputStreamReader isr = new InputStreamReader(fis);

//      InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));

//      InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"), "utf-8");

//      InputStreamReader isr = new InputStreamReader(new FileInputStream("b.txt"), "gbk");

}

OutputStreamWriter

OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

OutputStreamWriter(OutputStream out) ;
// 创建使用默认字符编码的 OutputStreamWriter。 
OutputStreamWriter(OutputStream out, Charset cs) ;
// 创建使用给定字符集的 OutputStreamWriter。

未命名绘图.jpg

多线程

概念

程序:是一个固定的资源集合,是安装在电脑上硬盘上的,是一个静态的概念。

进程:杀死一个正在执行的程序。是程序先电脑申请资源的一个单位。(比如打开的LOL,就是一个进程,LOL会以这个进程想系统申请内存和Cpu等等)

注意:进程是资源管理器中可以查看的。

并行和并发

并行:多个CPU或者多核的情况下,程序的同时执行,这种同时执行是物理上的同时执行。

并发:单个CPU的情况下,程序的同时执行,这种执行是逻辑上的同时执行。

如何创建多线程

创建线程肯定需要与操作系统进行交互。向操作系统申请相关资源,然后才能开启。而Java语言不能和操作系统直接交互,能和操作系统直接进行交互的语言有C\C++\汇编。而java语言是以c/c++为基础建立起来的。java中有相关的类和方法可以建立多线程这个类就是Thread类。

第一种:继承Thread类

  1. 定义一个类,然后继承Thread类
  2. 在类中重写Thread类的run方法
  3. 启动此类的实例
package top.zunmx.d2;

// ALC-Master
class MyThread extends Thread {
    @Override
    public void run() {
        /*
         * 注意事项 1. 线程中一般放的都是耗时比较长的操作
         */

        for (int i = 0; i < 10; i++) {
            System.out.println(super.getId() + "--R-->" + i);
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        /*
         * 注意事项2 启动线程用的是start方法,而不是run方法(run方法置时调用而已)
         */
        mt.run(); // 错误,这个只是调用
        mt.start(); // 正确,Java虚拟机调用线程的方法
//      mt.start(); // 错误, java.lang.IllegalThreadStateException
    }
}

注意事项

  1. 线程中一般放的都是耗时比较长的操作
  2. 启动线程用的是start方法,而不是run方法(run方法置时调用而已)
  3. 线程只能启动一次

常用方法

void setName(String name); // 改变线程名称,使之与参数name相同
String getName();// 返回的线程的名称
static Thread currentThread(); //返回对当前正在执行的线程对象的引用
void setPriority(int newPriority);// 更改线程的优先级
void setDaemon(boolean b); //如果都是守护线程,Java虚拟机就会退出,必须在启动前调用
package top.zunmx.d2;
// ALC-Master
class Mythread3 extends Thread{

    @Override
    public void run() {
        for(int i =0;i<10;i++) {
            System.out.println(getName()+" > - - - - - > "+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}
public class Demo3 {
    public static void main(String[] args) {
        Mythread3 mt1 = new Mythread3();
        Mythread3 mt2 = new Mythread3();
        mt1.setName("多1");
        mt2.setName("多2");
        Thread.currentThread().setName("主线程");
        mt1.start();
        mt2.start();
        for(int i =0;i<10;i++) {
            System.out.println(Thread.currentThread().getName()+" > - - - - - > "+i);
        }
    }
}

优先级

优先级越高先执行的概率就越大

java.lang.[Thread]

数据类型 常量 值
public static final int MAX_PRIORITY 10
public static final int MIN_PRIORITY 1
public static final int NORM_PRIORITY 5

多线程(续)

构造方法

Thread() ;
    //分配新的 Thread 对象。 
Thread(Runnable target) ;
    //分配新的 Thread 对象。 
Thread(Runnable target, String name) ;
    //分配新的 Thread 对象。 
Thread(String name) ;
    //分配新的 Thread 对象。

第一种方式

继承Thread类

第二种实现方式

Runnable接口

步骤

  1. 写一个类然后实现Runnable接口
  2. 重写在此类中的run方法
  3. 创建此类的对象
  4. 将此类的对象当作参数传递给Thread类的构造方法,来构造Thread类对象。
  5. 通过Thread类对象启动线程
package top.zunmx.u1;

// ALC-Master
/*
1. 写一个类然后实现Runnable接口
2. 重写在此类中的run方法
3. 创建此类的对象
4. 将此类的对象当作参数传递给Thread类的构造方法,来构造Thread类对象。
5. 通过Thread类对象启动线程
*/
// 1 
class MyRun implements Runnable {
    // 2
    @Override
    public void run() {
        for (int i = 0; i < 1; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

public class Demo1 {
    public static void main(String[] args) {
        // 3
        MyRun mr = new MyRun();
        // 4
        Thread t = new Thread(mr);
        // 5
        t.start();
    }
}

第三种方式

使用匿名内部类

格式:

new 类名或接口名(){

}

本质:匿名内部类就是一个类或接口的对象。

Thread匿名内部类

new Thread("线程名1") {
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName() + "--->" + i);
        }
    };
}.start();

Runnable接口的匿名内部类

new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}, "线程名2").start();

多线程中遇到的安全性问题

出现问题的原因:

  1. 有多线程的环境
  2. 有共享的数据
  3. 多个线程同时操作这个共享的数据

经过分析,只能解决第3个环境,就是不让多个线程不能同时这个共享的数据。

也就是只能一个线程操作这个数据,这个线程操作的时候,其他线程只能等着,等这个线程执行完毕之后,其他线程才能去执行。

同步代码块

格式:

synchronized (锁){
    需要同步的代码
}

锁就是一个对象,但是必须要保证是同一个对象。可以类比火车上的厕所

同步方法

普通同步方法

权限修饰符 synchronized 返回值类型 方法名(){

}
锁是谁? this

静态同步方法

权限修饰符 synchronized static 返回值类型 方法名(){

}
锁是谁? 类名.class

之前见过带同步的类

  • StringBuffer
  • Vector
  • Hashtable

线程的声明周期

线程是一个动态的概念,它有创建的时候,也有运行和变化的时候,当然也有销毁的时候,这些变化的状态被我们称作现成的声明周期。

  • 新建态:使用new关键字创建出来的时候
  • 就绪态:通过start方法有了系统分配的资源,等待CPU来临。
  • 运行态:CPU来临了,正在运行
  • 阻塞态:遇到I/O或者Sleep或者wait等方法,会进入此状态
  • 销毁态:执行完毕

Java中关于线程声明周期的描述

​ 上面说的几种状态都是理论上的状态,实际上在Java中有对线程生命周期更精确的描述,Java将这些状态封装成了对象,可以通过Thread直接访问。

获取线程的状态

Thread.State getState()

通过Thread类中的getState方法可以获取Java中线程状态的类 Thread.State

NEW 至今尚未启动的线程处于这种状态。
RUNNABLE 正在 Java 虚拟机中执行的线程处于这种状态。
BLOCKED 受阻塞并等待某个监视器锁的线程处于这种状态。
WAITING 无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。
TIMED_WAITING 等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。
TERMINATED 已退出的线程处于这种状态。

在给定时间点上,一个线程只能处于一种状态。这些状态是虚拟机状态,它们并没有反映所有操作系统线程状态。

线程池

比如说有一个工作,工作中分了几个任务需要用多个线程来执行。我们知道创建一个线程是需要和操作系统进行交互并耗费系统资源,尤其是特别小的任务更为明显(因为他们需要创建大量的线程,但是又用不了多久,用完之后就销毁。在这个过程中浪费了大量的系统资源)所以说Java中就提出了线程池的概念,线程池就是一个装线程的池子。线程池可以先在池子中维护指定个数的线程,有任务就去执行,任务执行完了就回到线程池中,而不是被销毁,继续等待下一个任务的执行。这样能极大的节省系统的资源或者说是能充分利用系统的资源。

使用步骤

  1. 获取线程池对象
   //获取线程池对象需要一个Executors工具类,通过调回用这个工具类的方法可以获取到线程池对象ExecutorService

   static ExecutorService newSingleThreadExecutor();
   static ExecutorService newFixedThreadPool(int nThreads);
   ```

```java
   //获取线程池对象
   ExecutorService pool = Executors.newSingleThreadExecutor(); //单个线程
   //构造,线程池容量
   ExecutorService pool2 = Executors.newFixedThreadPool(3); //指定个数
   ```
2. 创建一个任务类对象

//可以使用Runnable创建一个任务类对象
```
3. 使用线程池的方法

java Future<?> submit(Runnable task) ; //提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future void shutdown() ; //启动一次顺序关闭,执行以前提交的任务,但不接受新任务。 List<Runnable> shutdownNow() ; //试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。


```java
package top.zunmx.d2;

// ALC-Master
public class Demo2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "----"+ i);
}
}
}
```

```java
// 获取线程池对象
//ExecutorService pool = Executors.newSingleThreadExecutor(); // 单个线程
// 构造,线程池容量
ExecutorService pool = Executors.newFixedThreadPool(3); // 指定个数

// 2 创建任务类对象
Demo2 mr = new Demo2();

// 3 提交任务类对象
pool.submit(mr);
pool.submit(mr);
pool.shutdown();
pool.submit(mr);
pool.submit(mr);
```

JDK8新特性

JDK8之前,接口中的方法只有抽象方法 public abstract 修饰

JDK8之后,接口中的方法除了有抽象方法之外,还可以有被static及default修时的方法

接口的作用就是定义规范(比如说一个狗必须要会唱歌,那么我们就可以在接口中定义一个方法【此时方法还是默认为抽象方法】,以后实现此接口的狗就必须要重写这个抽象方法,这也就是所谓的规范),但是随着时代的发展,接口中的内容还有可能会增多。但是一旦还是按照之前的方式(之前的方式就是接口中全是抽象方法)来定义方法,那么势必会影响到之前已经写好的项目(因为之前的项目用的就是这个接口,那么肯定要重写这些新的抽象方法,如果不重写,那么项目就报错不能运行)。所以为了之前的项目,JDK8中就允许可以定义非抽象方法了,但是这些方法只能被static或default修时。

default

  1. 被default修饰的方法必须有方法体,并且能通过接口的子类对象进行访问
  2. 如果一个子类同时实现了多个接口,并且多个接口中有相同生命的default方法,那么这个default方法必须重写,并且访问的也是子类重写之后的default方法
    - 如果我们向访问其中某一个接口中的方法,那么就用接口名.super.default修饰的方法名。例如[SuperDog.super.dance()]

static

在接口中被static修饰的方法,只能通过接口名.方法名()访问。

评论 0

还没有评论,来说点什么吧。

提交后需要审核才会公开。