java判断字符串终点,JAVA高级学习笔记

JAVA高级笔记

程序、进程、线程

b1d5094ea80f73de3882b501119f5d78.png了解

fee341081587b14fa87ad8cd6ad19617.png优点

0667a952e84d98e3ef8aea2786af3645.png

创建多线程方式一:继承Thread类创建一个继承与Thread类的子类

重写Thread类的run() 方法 --> 将此线程执行的操作声明在run() 中

创建Thread类的子类的对象

通过此对象去调用Thread的start() 方法public class ThreadTest {

public static void main(String[] args) {

MyThread myThread = new MyThread();

// 不能使用myThread.run()来调用,这样就是单线程了

myThread.start();

for (int i = 0; i 

if (i % 2 == 0) {

System.out.println(i + "abc");

}

}

}

}

class MyThread extends Thread{

@Override

public void run() {

for (int i = 0; i 

if (i % 2 == 0) {

System.out.println(i);

}

}

}

}要想启动线程,调用start() 方法,不能用run() 方法来调用

同一个对象不能调用两次start() 方法,想用同一套线程方案,需要再重新创建一个对象

线程的常用方法start() :启动当前线程,调用当前线程的run()

run() :通常需要重写Thread类中的此方法,将创建的线程要执行的操作

currentThread() :静态方法,返回执行当前代码的线程

getName() :获取当前线程的名字sout(Thread.currentThread().getName());

setName() :设置当前线程的名字对象名.setName("名称");

// 给主线程命名

Thread.currentThread().setName("主线程");

还可以用构造器的方式给主线程命名

yield() :释放当前cpu的执行权,有可能在下一刻又会被该线程执行

join() :在线程a中,调用线程b的join() 方法,此时线程a就进入阻塞状态,知道线程b完全执行完之后,线程a才结束阻塞状态例如在线程a中,调用线程b.join();(本身会抛出异常,需要用try-catch),执行join方法时a线程将就会进入阻塞状态,等b线程执行完之后才继续执行a线程

sleep(long millitime) :让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前线城市阻塞状态

isAlive() :判断当前线程是否存活

线程优先级

MAX_PRIORITY: 10 --> 较高优先级

MIN_PRIORITY: 1 --> 较低优先级

NORM_PRIORITY: 5 --> 默认优先级getPriority() : 获取线程的优先级

setPriority(int priority) : 设置线程的优先级高优先级的线程要抢占低优先级cpu的执行权。但是只是从概率上讲的。

高优先级的线程高概率的情况下被执行,并不意味着只有当高优先级的线程执行完之后,低优先级的线程才执行。

卖票练习/**

* @ClassName ThreadTest2

* @Description 卖票练习测试

* @Author LiangHui

* @Date 2020/7/16 8:42

* @Version V1.0

*/

public class ThreadTest2 {

public static void main(String[] args) {

Window t1 = new Window();

Window t2 = new Window();

Window t3 = new Window();

// 线程命名

t1.setName("窗口一");

t2.setName("窗口二");

t3.setName("窗口三");

t1.start();

t2.start();

t3.start();

}

}

class Window extends Thread{

private static int ticket = 100;

@Override

public void run() {

while (true) {

if (ticket > 0) {

// 此处还存在线程安全问题,或同时有多个线程进入if中

System.out.println(getName() + " 卖票,票号为:" + ticket);

ticket--;

}else {

break;

}

}

}

}

创建多线程方式二:实现Runnable接口创建一个实现Runnable接口的类

实现类去实现Runnable中的抽象方法:run()

创建实现类的对象

将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象

通过Thread类的对象调用start()public class ThreadTest3 {

/**

* @Author LiangHui

* @Description main方法,主程序入口

* @Date 2020/7/16 10:13

* @param args

* @return void

*/

public static void main(String[] args) {

// 创建实现类的对象

MyThread3 myThread3 = new MyThread3();

// 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象

Thread t1 = new Thread(myThread3);

// 通过Thread类的对象调用start()

// 要看源码才知道为什么会执行MyThread类中的run()方法

// 因为target不为null 所以调用了Runnable类型的target的run()方法

t1.start();

}

}

class MyThread3 implements Runnable{

/**

* @Author LiangHui

* @Description 实现run()抽象方法

* @Date 2020/7/16 10:14

* @param

* @return void

*/

public void run() {

for (int i = 0; i 

if (i % 2 == 0) {

// currentThread() 获取当前线程

System.out.println(Thread.currentThread().getName() + "偶数" + i);

}

}

}

}

卖票练习public class ThreadTest4 {

public static void main(String[] args) {

Window2 window2 = new Window2();

Thread t1 = new Thread(window2);

Thread t2 = new Thread(window2);

Thread t3 = new Thread(window2);

t1.setName("窗口一");

t2.setName("窗口二");

t3.setName("窗口三");

t1.start();

t2.start();

t3.start();

}

}

class Window2 implements Runnable {

// 因为只创建了一个Windows2对象,所以不用添加static

private int ticket = 100;

/**

* @Author LiangHui

* @Description 实现run()抽象方法,实现卖票功能

* @Date 2020/7/16 10:40

* @param

* @return void

*/

@Override

public void run() {

while (true) {

if (ticket > 0) {

System.out.println(Thread.currentThread().getName() + " 票号:" + ticket);

ticket--;

} else {

break;

}

}

}

}

比较两种创建线程的方式开发中,优先选择实现先Runnable接口的方式实现的方式没有类的单继承性的局限性

实现的方式更适合来处理多个线程有共享数据的情况

联系Thread类也是实现了Runnable接口的public class Thread implements Runnable

两种方法都需要重写run() 方法,将线程要执行的逻辑声明在run() 中

线程的生命周期

28a9797f7330b22ccdde8cb830bdcda2.png

2e187d0ff2f4674dee443811d7095d37.png

线程安全问题问题:卖票过程中,出现了重票、错票 -->出现了线程的安全问题

问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票。

如何解决:当一个线程a在操作ticket的时候,其他线程不能参与进来。直到线程a操作完ticket时,其他线程才可以开始操作ticket。这种情况即使线程a出现了阻塞,也不能被改变。在Java中,我们通过同步机制,来解决线程的安全问题

方式一:同步代码块synchronized(同步监视器){

// 需要被同步的代码

}说明:操作共享数据的代码,即为需要被同步的代码。 --> 不能包含代码多了,也不能包含代码少了。

共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。

同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。

要求:多个线程必须要共用同一把锁。即同步监视器必须是相同的对象

补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。

同步代码块处理实现Runnablepublic class ThreadTest5 {

/**

* @Author LiangHui

* @Description main方法,主程序入口

* @Date 2020/7/16 18:05

* @param args

* @return void

*/

public static void main(String[] args) {

Window3 window3 = new Window3();

Thread t1 = new Thread(window3);

Thread t2 = new Thread(window3);

Thread t3 = new Thread(window3);

t1.setName("窗口一");

t2.setName("窗口二");

t3.setName("窗口三");

t1.start();

t2.start();

t3.start();

}

}

class Window3 implements Runnable{

private int ticket = 100;

/**

* @Author LiangHui

* @Description 实现run()方法,实现卖票功能

* @Date 2020/7/16 17:30

* @param

* @return void

*/

@Override

public void run() {

// 在操作共享变量的地方添加同步代码块

// synchronized(同步监视器),同步监视器可以使任意的对象,前提是必须是公用的对象(锁)

while (true) {

synchronized (this) {

if (ticket > 0) {

System.out.println(Thread.currentThread().getName() + " 票号:" + ticket);

ticket--;

} else {

break;

}

}

}

}

}

同步代码块处理继承Thread类class Window4 extends Thread {

private static int ticket = 100;

/**

* @Author LiangHui

* @Description 重写run()方法,实现卖票功能

* @Date 2020/7/17 7:15

* @param

* @return void

*/

@Override

public void run() {

// 类也是对象,所以Window4.class是对象,并且是唯一的

while (true) {

synchronized (Window4.class) {

if (ticket > 0) {

System.out.println(getName() + " 票号:" + ticket);

ticket--;

} else {

break;

}

}

}

}

}

方式二:同步方法如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的

同步方法块处理实现Runnableclass Window5 implements Runnable {

private int ticket = 100;

/**

* @Author LiangHui

* @Description 实现run()抽象方法,实现卖票功能

* @Date 2020/7/16 10:40

* @param

* @return void

*/

@Override

public void run() { // 同步方法,不能在run()方法中添加同步监视器

while (true) {

buyTicket();

}

}

/**

* @Author LiangHui

* @Description 创建同步方法,将操作共享变量的代码包含进去

* @Date 2020/7/17 8:57

* @param

* @return void

*/

private synchronized void buyTicket(){

if (ticket > 0) {

System.out.println(Thread.currentThread().getName() + " 票号:" + ticket);

ticket--;

}

}

}

同步代码块处理继承Thread类class Window6 extends Thread{

private static int ticket = 100;

/**

* @Author LiangHui

* @Description 重写run()方法,实现卖票功能

* @Date 2020/7/17 9:04

* @param

* @return void

*/

@Override

public void run() {

while (true) {

buyTicket();

}

}

/**

* @Author LiangHui

* @Description 创建静态的同步方法,将操作共享变量的代码包含进去

* @Date 2020/7/17 9:07

* @param

* @return void

*/

private static synchronized void buyTicket() {

if (ticket > 0) {

System.out.println(Thread.currentThread().getName() + " 票号:" + ticket);

ticket--;

}

}

}

使用同步方式的优劣同步的方式,解决了线程的安全问题。—好处

操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。 —局限性

解决单例模式的懒汉式实现中的线程安全问题public class SingletonLazyTest {

public static void main(String[] args) {

Bank bank = Bank.getInstance();

}

}

class Bank {

private Bank() {

}

// 私有化构造器

// 内部创建类的对象,并且为空

private static Bank instance = null;

// 同步方法,锁(同步监视器)-->Bank.class

// public static synchronized Bank getInstance(){

public static Bank getInstance(){

// // 存在线程安全问题

// if (instance == null) {

//     instance = new Bank();

// }

// return instance;

// 同步代码块

// 方式一:效率稍差,每次new对象的时候,都要排队拿锁,等候时间长,效率稍差

// 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;

}

}

完整版public class SingletonLazyTest {

public static void main(String[] args) {

Bank bank = Bank.getInstance();

}

}

class Bank {

private Bank() {

}

private static Bank instance = null;

public static Bank getInstance(){

if (instance == null) {

synchronized (Bank.class) {

if (instance == null) {

instance = new Bank();

}

}

}

return instance;

}

}

线程死锁问题

3edb06fc71e981f84675b2c64292f532.png

演示死锁问题public class ThreadTest9 {

public static void main(String[] args) {

StringBuffer str1 = new StringBuffer();

StringBuffer str2 = new StringBuffer();

new Thread(){

@Override

public void run() {

synchronized (str1) {

str1.append("a");

str2.append(1);

try {

sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized (str2) {

str1.append("b");

str2.append(2);

System.out.println(str1);

System.out.println(str2);

}

}

}

}.start();

new Thread(new Runnable() {

@Override

public void run() {

synchronized (str2) {

str1.append("c");

str2.append(3);

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

synchronized (str1) {

str1.append("d");

str2.append(4);

System.out.println(str1);

System.out.println(str2);

}

}

}

}).start();

}

}线程a拿到str1的锁(同步监视器),因为有嵌套的同步代码块,所以要结束当前a线程,必须要拿到str2的锁。

而线程b与线程a同时进行,b线程拿到str2的锁,也是因为嵌套的同步代码块,所有要结束当前b线程,必须要拿到str1的锁

因为a线程拿到了str1的锁,所以b线程无法得到str1的锁,所以b线程阻塞,等待str1的锁释放

同理,b线程拿到str2的锁,所以a线程无法得到str2的锁,所以a线程阻塞,等待str2的锁释放

两个线程各自拿着对方的锁,所以造成线程死锁

解决方法专门的算法、原则

尽量减少同步资源的定义

尽量避免嵌套同步

线程安全问题 — JDK5.0新增

方式三:Lock锁public class ThreadCreateLockTest {

public static void main(String[] args) {

Window7 window7 = new Window7();

Thread t1 = new Thread(window7);

Thread t2 = new Thread(window7);

Thread t3 = new Thread(window7);

t1.setName("窗口一");

t2.setName("窗口二");

t3.setName("窗口三");

t1.start();

t2.start();

t3.start();

}

}

class Window7 implements Runnable{

private int ticket = 100;

// 实例化ReentrantLock

private ReentrantLock lock = new ReentrantLock();

// 实例化ReentrantLock,参数为true,让他们公平竞争,当线程a出来之后,不会立马重新进行争夺,等后面的进程完成之后再重新争夺

//private ReemtrantLock lock = new ReentrantLock(true);

@Override

public void run() {

while (true) {

try {

// 调用锁定方法lock()

lock.lock();

if (ticket > 0) {

System.out.println(Thread.currentThread().getName() + " 票号为:" + ticket);

ticket--;

} else {

break;

}

} finally {

// 调用解锁方法unlock()

lock.unlock();

}

}

}

}在线程中实例化ReentranLock对象,然后调用lock() 方法上锁,再调用unlock() 方法解锁。

操作的代码部分要用try-finally包含,finally中写unlock();

synchronized与Lock对比

f0cf93fe8a79a49aa7146e973bfef011.png

线程面试题1synchronized与lock的异同synchronized机制在执行完相应的同步代码之后,自动释放同步监视器

lock需要手动的启动同步监视器( lock() ),同时结束同步也需要手动的实现( unlock() )相同:二者都可以解决线程安全问题

不同

线程的通信线程之间的交互

使用wait()和notify()/notifyAll()wait() 将线程进入阻塞状态,同时线程释放同步监视器

notify() 执行此方法,就会唤醒被wait() 的一个线程,如果有多个线程被wait() 那么就会根据优先级唤醒一个线程

notifyAll() 将所有被wait() 的线程唤醒

说明这些方法代码块只能在同步代码块或同步方法当中

这些方法的调用者,必须是同步代码块或同步方法中的同步监视器

否则会出现IllegalMonitorStateException异常

这些方法不是定义在Thread中,而是定义在java.lang.Object 中(因为调用者可以是任何对象)// 两个线程交替打印1~100的数

while(true){

synchronized(this){

this.notify();//调用者和同步监视器相同,如果都是this可省略

//notifyAll();

if(number <= 100){

sout(Thread.currentThread.getName() + "打印:" + number);

number++;

try{

this.wait();//调用者和同步监视器相同,如果都是this可省略

}catch(InterruptedException e){

e.printStackTrace();

}

}else{

break;

}

}

}

sleep()和wait() 异同相同点两个方法都可以使得当前的线程进入阻塞状态

不同点sleep() 执行之后不会释放同步监视器

wait() 执行之后会释放同步监视器sleep() 可以在任意需要的场景下调用

wait() 必须使用在同步代码块或同步方法中Thread类中声明sleep() ,Object类中声明wait()两个方法声明的位置不同

调用的要求不同

如果两个方法都声明在同步代码块或同步方法中,是否会释放同步监视器

线程通信经典例题:生产者与消费者问题

64bac21fb3ff96fe7f0c67dddce64574.pngpublic class ThreadProducerConsumerTest {

public static void main(String[] args) {

Clerk clerk = new Clerk();

Producer p1 = new Producer(clerk);

Thread pt1 = new Thread(p1);

pt1.setName("生产者1");

pt1.start();

Consumer c1 = new Consumer(clerk);

Thread ct1 = new Thread(c1);

ct1.setName("消费者1");

ct1.start();

}

}

/**

* 店铺

*/

class Clerk{

private int productCount = 0;

/**

* @Author LiangHui

* @Description 进货

* @Date 2020/7/17 17:40

* @param

* @return void

*/

public synchronized void production() {

if (productCount 

productCount++;

System.out.println(Thread.currentThread().getName() + "生产产品 " + productCount);

// 每生产一个产品就可以将所有消费者线程唤醒,防止同类之间相互唤醒导致假死状态

notifyAll();

} else {

try {

wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

/**

* @Author LiangHui

* @Description 卖出

* @Date 2020/7/17 17:40

* @param

* @return void

*/

public synchronized void consumption() {

if (productCount > 0) {

System.out.println(Thread.currentThread().getName() + "消费产品 " + productCount);

productCount--;

// 每消费一个产品就可以将所有生产者线程唤醒,防止同类之间相互唤醒导致假死状态

notifyAll();

} else {

try {

wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

/**

* 生产者

*/

class Producer implements Runnable {

private Clerk clerk;

public Producer(Clerk clerk) {

this.clerk = clerk;

}

/**

* @Author LiangHui

* @Description 调用production方法进行生产

* @Date 2020/7/17 17:47

* @param

* @return void

*/

@Override

public void run() {

while (true) {

clerk.production();

}

}

}

/**

* 消费者

*/

class Consumer implements Runnable {

private Clerk clerk;

public Consumer(Clerk clerk) {

this.clerk = clerk;

}

/**

* @Author LiangHui

* @Description 调用consumption方法进行消费

* @Date 2020/7/17 17:48

* @param

* @return void

*/

@Override

public void run() {

while (true) {

clerk.consumption();

}

}

}生产者和消费者扶着负责生产和消费,而生产和消费的具体体现就是店铺进货和卖出,所以将具体的实现方法写在了店铺类中

生产和消费无限进行,所以在run() 中写入while(true) 语句

每生产或消费一个产品就可以将所有生产者或消费者线程唤醒,防止同类之间相互唤醒,导致假死状态,所以使用notifyAll()

创建多线程方式三:实现Callable接口 — JDK5.0新增创建一个实现Callable接口的实现类

实现call() 方法,将此线程需要执行的操作声明在call() 方法中

创建Callable接口实现类的对象

将此Callable接口实现类的对象,作为参数传递到FutureTask构造器中,创建FutureTask的对象

将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start() 方法

如果需要返回值:获取Callable中的call方法的返回值

get() 返回值即为FutureTask构造器参数callable实现类重写的call() 的返回值public class ThreadCreateCallableTest {

public static void main(String[] args) {

// 3.创建Callable接口实现类的对象

MyThread4 myThread4 = new MyThread4();

// 4.将此Callable接口实现类的对象作为参数传递到FutureTask构造器中,创建FutureTask的对象

FutureTask futureTask1 = new FutureTask(myThread4);

FutureTask futureTask2 = new FutureTask(myThread4);

// 5.将FutureTask的对象作为参数传递到Thread类中,创建Thread对象,并调用start()

Thread t1 = new Thread(futureTask1);

Thread t2 = new Thread(futureTask2);

t1.setName("线程1");

t2.setName("线程2");

t1.start();

t2.start();

// 6.获取Callable中call方法的返回值

// get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值

try {

Object sum1 = futureTask1.get();

Object sum2 = futureTask2.get();

System.out.println("总和为:" + sum1 + "," + sum2);

} catch (InterruptedException e) {

e.printStackTrace();

} catch (ExecutionException e) {

e.printStackTrace();

}

}

}

// 1.创建一个实现Callable的实现类

class MyThread4 implements Callable {

/**

* @Author LiangHui

* @Description 2.重写call()方法 将100以内的偶数累加

* @Date 2020/7/17 22:40

* @param

* @return java.lang.Object

*/

@Override

public Object call() throws Exception {

int sum = 0;

for (int i = 0; i <= 100; i++) {

if (i % 2 == 0) {

System.out.println(Thread.currentThread().getName() + "偶数" + i);

sum += i;

}

}

return sum;

}

}如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大?call() 可以有返回值

call() 可以抛出异常

Callable是支持泛型的

创建多线程方式四:使用线程池 — JDK5.0新增开发中常用线程池

更多的是用框架来实现的

984b2f45cb64714698975c102359801b.png

6ed424b7e284665353ea82ed8585c2d8.pngExecutorService

Executors根据需求创建线程池,提供指定线程数量的线程池

ExecutorService service = Executors.newFixedThreadPool(10);

用实现Runnable或者实现Callable的方式写线程的操作

将实现类创建的对象放到线程池中Runnable使用:service.execute();

Callable使用:service.submit(); 可以接收返回值,要用FutureTask接收,然后get()

关闭线程池:service.shutdown();

设置线程池属性因为创建线程池返回的对象赋给的是ExecutorService接口,属性方法少,所以要对接口对象进行强转,强转为接口的实现类ExecutorService service = Executors.newFixedThreadPool(10);

ThreadPoolExecutor service1 = (ThreadPoolExecutor)service;

// 设置属性

service1.setCorePoolSize(15);

...

6cafe54280f47dcc6fb7c1addc305bf7.png

f9b1bbbcc16f4db24c244d31c961fe67.png

1、String类

概述String声明为final的,不可被继承

String实现了Serializable接口:表示字符串是支持序列化的

? 实现了Comparable接口:表示String可以比较大小

String内部定义了final char[] value用于存储字符串数据

String:代表不可变的字符序列。简称:不可变性。

体现:只要对字符串进行修改,都是重新在方法区(含字符串常量池)中新建当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。

当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。

当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。

通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。

字符串常量池中是不会存储相同内容的字符串的。

String str1 = “abc”;与string str2 = new String(“abc”);的区别

27fc32bcbd2d37c8cd1f437e6fe42fe1.png

练习1

2d47519d1edaf3e1bac7352d15f901e3.png

面试题:String s = new String(“abc”);方式创建对象,在内存中创建了几个对象

答:两个。一个是堆空间中的new的结构,另一个是char[]对应的常量池中的数据:“abc”。

字符串特性

e629ae4119130b2b7c35585d06d6cb8f.pngintern()方法

如果String声明为final 那么拼接的话再比较就是true

面试题1public class StringTest {

String str = new String("good");

char[] ch = { 't', 'e', 's', 't' };

public void change(String str, char ch[]) {

str = "test ok";

ch[0] = 'b';

}

public static void main(String[] args) {

StringTest ex = new StringTest();

ex.change(ex.str, ex.ch);

System.out.println(ex.str); // good

System.out.println(ex.ch); // best

}

}

String常用方法1int length():返回字符串的长度: return value.length

char charAt(int index): 返回某索引处的字符return value[index]

boolean isEmpty():判断是否是空字符串:return value.length == 0

String toLowerCase()

String toUpperCase()

String trim()

boolean equals(Object obj):比较字符串的内容是否相同

boolean equalsIgnoreCase(String anotherString)

String concat(String str)

int compareTo(String anotherString)

String substring(int beginIndex)

String substring(int beginIndex, int endIndex)

String常用方法2boolean endsWith(String suffix)

boolean startsWith(String prefix)

boolean startsWith(String prefix, int toffset)

boolean contains(CharSequence s)

int indexOf(String str)

int indexOf(String str, int fromIndex)

int lastIndexOf(String str)

int lastIndexOf(String str, int fromIndex)

注:indexOf和lastIndexOf方法如果未找到都是返回-1当indexOf(str)和lastIndexOf(str)相同时,要不字符串中没有,要不就只有一个

所以可以用这个来判断字符串中指定的子字符串是否只有一个

String常用方法3替换:

String replace(char oldChar, char newChar)

String replace(CharSequence target, CharSequence replacement)

String replaceAll(String regex, String replacement)

String replaceFirst(String regex, String replacement)

匹配:

boolean matches(String regex)

切片:

String[] split(String regex)

String[] split(String regex, int limit)

String常用方法整理 (没记牢的)String toLowerCase():使用默认语言环境,将 String 中的所有字符转换为小写

String toUpperCase():使用默认语言环境,将 String 中的所有字符转换为

String trim():返回字符串的副本,忽略前导空白和尾部空白

boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写

String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+”

int compareTo(String anotherString):比较两个字符串的大小

String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。

String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。即左闭右开

boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束

boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始

boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始

boolean contains(CharSequence s):当字符串中包含此序列时,返回true

int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引

int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始

int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引

int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索

String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。

String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。

String replaceAll(String regex, String replacement):使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。

String replaceFirst(String regex, String replacement):使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。

boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。

String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。

String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。

String 与 char[]之间的转换

String --> char[]:调用String的toCharArray()

char[] --> String:调用String的构造器

String 与 byte[]之间的转换

编码:String --> byte[]:调用String的getBytes()

解码:byte[] --> String:调用String的构造器public void test3() throws UnsupportedEncodingException {

String str1 = "abc123中国";

byte[] bytes = str1.getBytes();//使用默认的字符集,进行编码。

System.out.println(Arrays.toString(bytes));

byte[] gbks = str1.getBytes("gbk");//使用gbk字符集进行编码。

System.out.println(Arrays.toString(gbks));

System.out.println("******************");

String str2 = new String(bytes);//使用默认的字符集,进行解码。

System.out.println(str2);

String str3 = new String(gbks);

System.out.println(str3);//出现乱码。原因:编码集和解码集不一致!

String str4 = new String(gbks, "gbk");

System.out.println(str4);//没有出现乱码。原因:编码集和解码集一致!

}

StringBuffer和StringBuilder异同String:不可变的字符序列

StringBuffer:可变的字符序列:线程安全的,效率低;

StringBuilder:可变的字符序列:jdk5.0新增,线程不安全的,效率高;可变的字符序列,在调用方法时,改变的是自身

StringBuffer底层实现创建StringBuffer时,是创建了一个char[16]的数组,是固定长度的

而String创建时,是创建了一个char[0]的数组,是根据参数来选择长度的

StringBuffer s1 = new StringBuffer(“abc”);

底层:char[] value = new char[“abc”.length() + 16]。

但是s1.length() 还是3

当需要扩容时,默认情况下,扩容为原来的2倍+2,同时将原有数组中的元素复制到新的数组中

在开发当中:使用StringBuffer

当知道大概字符串长度时,创建StringBuffer对象时,最好规定长度,避免扩容复制,提高效率

StringBuffer s = new StringBuffer(int capacity);

StringBuilder s = new StringBuilder(int capacity);

StringBuffer常用方法StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接

StringBuffer delete(int start,int end):删除指定位置的内容

StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str

StringBuffer insert(int offset, xxx):在指定位置插入xxx

StringBuffer reverse() :把当前字符序列逆转

public int indexOf(String str) : 返回查找的子字符串的索引

public String substring(int start,int end) : 返回一个从start开始到end索引结束的左闭右开区间的子字符串

public int length() : 返回长度

public char charAt(int n ) : 返回索引位置的字符

public void setCharAt(int n ,char ch) : 更改索引位置的字符

String算法题-1

2、JDK8之前的日期时间API

System类中的currentTimeMillis()long time = System.currentTimeMillis();

// 返回当前时间与1970年1月1日0时0分0喵=秒之间以毫秒为单位的时间差

// 称为时间戳

// 创建两个时间戳相差,得出运行时间,比较效率等

Date类java.util.Date类

构造器一:Date()Date date1 = new Date();

sout(date1.toString()); // Wed Jul 22 15:56:20 CST 2020

sout(date1.getTime()); // 获取时间戳(同currentTimeMillis)

构造器二:Date(long date)将getTime获得的毫秒数转换成时间,将指定毫秒数换算成时间java.sql.Date类,父类是java.util.Date类java.sql.Date date1 = new java.sql.Date(231654489456L);

System.out.println(date1);java.util.Date转为java.sql.DateDate date1 = new Date();

java.sql.Date date2 = new java.sql.Date(date1.getTime());

SimpleDateFormat类java.text.SimpleDateFormat

4d1203b04ea3cc5d7f1ec5b488f622a5.pngpublic void test3() throws ParseException {

Date date = new Date();

System.out.println("使用默认构造器");

SimpleDateFormat sdf = new SimpleDateFormat();

// 格式化

String format1 = sdf.format(date);

System.out.println("格式化:" + format1);

// 解析

Date date2 = sdf.parse("20-7-23 下午4:00");

System.out.println("解析:" + date2);

System.out.println();

System.out.println("使用指定格式的构造器");

SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

String format2 = sdf2.format(date);

System.out.println("格式化:" + format2);

Date date3 = sdf2.parse("2020-07-22 12:00:00");

System.out.println("解析:" + date3);

}开发中常用指定格式的构造器

”2020-07-31“ 转换为 java.sql.Date类public void test4() throws ParseException {

String str = "2020-07-31";

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

Date date = sdf.parse(str);

java.sql.Date dateSql = new java.sql.Date(date.getTime());

System.out.println(dateSql);

}

java.util.Calendar(日历)类,抽象类

3042af5217a6eced0554478263331005.pngpublic void testCalendar(){

//1.实例化

//方式一:创建其子类(GregorianCalendar)的对象

//方式二:调用其静态方法getInstance()

Calendar calendar = Calendar.getInstance();

//        System.out.println(calendar.getClass());

//2.常用方法

//get()

int days = calendar.get(Calendar.DAY_OF_MONTH);

System.out.println(days);

System.out.println(calendar.get(Calendar.DAY_OF_YEAR));

//set()

//calendar可变性

calendar.set(Calendar.DAY_OF_MONTH,22);

days = calendar.get(Calendar.DAY_OF_MONTH);

System.out.println(days);

//add()

calendar.add(Calendar.DAY_OF_MONTH,-3);

days = calendar.get(Calendar.DAY_OF_MONTH);

System.out.println(days);

//getTime():日历类---> Date

Date date = calendar.getTime();

System.out.println(date);

//setTime():Date ---> 日历类

Date date1 = new Date();

calendar.setTime(date1);

days = calendar.get(Calendar.DAY_OF_MONTH);

System.out.println(days);

}

3、JDK8中的日期时间API

java.time APILocalDate

LocalTime

LocalDateTime高频

方法now ()获取当前的日期、时间、日期+时间LocalDate localDate = LocalDate.now();

LocalTime localTime = LocalTime.now();

LocalDateTime localDateTime = LocalDateTime.now();

of ()设置指定的年、月、日、时、分、秒。没有偏移量LocalDateTime localDateTime1 = LocalDateTime.of(2020, 10, 6, 13, 23, 43);

getXxx()获取相关的属性System.out.println(localDateTime.getDayOfMonth());

System.out.println(localDateTime.getDayOfWeek());

System.out.println(localDateTime.getMonth());

System.out.println(localDateTime.getMonthValue());

System.out.println(localDateTime.getMinute());

withXxx()设置相关的属性LocalDate localDate1 = localDate.withDayOfMonth(22);

LocalDateTime localDateTime2 = localDateTime.withHour(4);

//不可变性

LocalDateTime localDateTime3 = localDateTime.plusMonths(3);

LocalDateTime localDateTime4 = localDateTime.minusDays(6);

Instant类似于java.util.Date类//now():获取本初子午线对应的标准时间

Instant instant = Instant.now();

System.out.println(instant);//2019-02-18T07:29:41.719Z

//添加时间的偏移量

OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));

System.out.println(offsetDateTime);//2019-02-18T15:32:50.611+08:00

//toEpochMilli():获取自1970年1月1日0时0分0秒(UTC)开始的毫秒数  ---> Date类的getTime()

long milli = instant.toEpochMilli();

System.out.println(milli);

//ofEpochMilli():通过给定的毫秒数,获取Instant实例  -->Date(long millis)

Instant instant1 = Instant.ofEpochMilli(1550475314878L);

System.out.println(instant1);

DateTimeFormat类似于SimpleDateFormat//方式一:预定义的标准格式。如:ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME

DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;

//格式化:日期-->字符串

LocalDateTime localDateTime = LocalDateTime.now();

String str1 = formatter.format(localDateTime);

System.out.println(localDateTime);

System.out.println(str1);//2019-02-18T15:42:18.797

//解析:字符串 -->日期

TemporalAccessor parse = formatter.parse("2019-02-18T15:42:18.797");

System.out.println(parse);

//方式二:

//本地化相关的格式。如:ofLocalizedDateTime()

//FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT :适用于LocalDateTime

DateTimeFormatter formatter1 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);

//格式化

String str2 = formatter1.format(localDateTime);

System.out.println(str2);//2019年2月18日 下午03时47分16秒

//本地化相关的格式。如:ofLocalizedDate()

//FormatStyle.FULL / FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT : 适用于LocalDate

DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM);

//格式化

String str3 = formatter2.format(LocalDate.now());

System.out.println(str3);//2019-2-18

//重点: 方式三:自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”)

DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");

//格式化

String str4 = formatter3.format(LocalDateTime.now());

System.out.println(str4);//2019-02-18 03:52:09

//解析

TemporalAccessor accessor = formatter3.parse("2019-02-18 03:52:09");

System.out.println(accessor);

4、比较器Comparable接口的使用举例: 自然排序像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象大小的方式。

像String、包装类重写compareTo()方法以后,进行了从小到大的排列

重写compareTo(obj)的规则:

如果当前对象this大于形参对象obj,则返回正整数,

如果当前对象this小于形参对象obj,则返回负整数,

如果当前对象this等于形参对象obj,则返回零。

对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj)方法。在compareTo(obj)方法中指明如何排序

Comparator接口的使用:定制排序背景:

当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,

或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,

那么可以考虑使用 Comparator 的对象来排序

重写compare(Object o1,Object o2)方法,比较o1和o2的大小:

如果方法返回正整数,则表示o1大于o2;

如果返回0,表示相等;

返回负整数,表示o1小于o2。/**

* 一、说明:Java中的对象,正常情况下,只能进行比较:==  或  != 。不能使用 > 或 

*          但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。

*          如何实现?使用两个接口中的任何一个:Comparable 或 Comparator

*

* 二、Comparable接口与Comparator的使用的对比:

*    Comparable接口的方式一旦一定,保证Comparable接口实现类的对象在任何位置都可以比较大小。

*    Comparator接口属于临时性的比较。

*/

public class CompareTest {

/*

Comparable接口的使用举例:  自然排序

1.像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象大小的方式。

2.像String、包装类重写compareTo()方法以后,进行了从小到大的排列

3. 重写compareTo(obj)的规则:

如果当前对象this大于形参对象obj,则返回正整数,

如果当前对象this小于形参对象obj,则返回负整数,

如果当前对象this等于形参对象obj,则返回零。

4. 对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj)方法。

在compareTo(obj)方法中指明如何排序

*/

@Test

public void test1(){

String[] arr = new String[]{"AA","CC","KK","MM","GG","JJ","DD"};

//

Arrays.sort(arr);

System.out.println(Arrays.toString(arr));

}

@Test

public void test2(){

Goods[] arr = new Goods[5];

arr[0] = new Goods("lenovoMouse",34);

arr[1] = new Goods("dellMouse",43);

arr[2] = new Goods("xiaomiMouse",12);

arr[3] = new Goods("huaweiMouse",65);

arr[4] = new Goods("microsoftMouse",43);

Arrays.sort(arr);

System.out.println(Arrays.toString(arr));

}

/*

Comparator接口的使用:定制排序

1.背景:

当元素的类型没有实现java.lang.Comparable接口而又不方便修改代码,

或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,

那么可以考虑使用 Comparator 的对象来排序

2.重写compare(Object o1,Object o2)方法,比较o1和o2的大小:

如果方法返回正整数,则表示o1大于o2;

如果返回0,表示相等;

返回负整数,表示o1小于o2。

*/

@Test

public void test3(){

String[] arr = new String[]{"AA","CC","KK","MM","GG","JJ","DD"};

Arrays.sort(arr,new Comparator(){

//按照字符串从大到小的顺序排列

@Override

public int compare(Object o1, Object o2) {

if(o1 instanceof String && o2 instanceof  String){

String s1 = (String) o1;

String s2 = (String) o2;

return -s1.compareTo(s2);

}

// return 0;

throw new RuntimeException("输入的数据类型不一致");

}

});

System.out.println(Arrays.toString(arr));

}

@Test

public void test4(){

Goods[] arr = new Goods[6];

arr[0] = new Goods("lenovoMouse",34);

arr[1] = new Goods("dellMouse",43);

arr[2] = new Goods("xiaomiMouse",12);

arr[3] = new Goods("huaweiMouse",65);

arr[4] = new Goods("huaweiMouse",224);

arr[5] = new Goods("microsoftMouse",43);

Arrays.sort(arr, new Comparator() {

//指明商品比较大小的方式:按照产品名称从低到高排序,再按照价格从高到低排序

@Override

public int compare(Object o1, Object o2) {

if(o1 instanceof Goods && o2 instanceof Goods){

Goods g1 = (Goods)o1;

Goods g2 = (Goods)o2;

if(g1.getName().equals(g2.getName())){

return -Double.compare(g1.getPrice(),g2.getPrice());

}else{

return g1.getName().compareTo(g2.getName());

}

}

throw new RuntimeException("输入的数据类型不一致");

}

});

System.out.println(Arrays.toString(arr));

}

}

5、其他类

System

Math

BigInteger

c12356b277fa45e5fab9d20e929fe81b.png

BigDecimal

c0fcd99a879fc057a2cdc36034955fdc.png

枚举类的使用类的对象只有有限个,确定。当需要定义一组常量时,强烈建议使用枚举类如果枚举类中只有一个对象,则可以作为单例模式的实现方式

JDK5.0之前,自定义枚举类public class SeasonTest1 {

public static void main(String[] args) {

Season spring = Season.SPRING;

System.out.println(spring);

}

}

// 枚举类

class Season {

// 1.声明Season对象的属性:private final修饰

private final String seasonName;

private final String seasonDesc;

// 2.私有化类的构造器,并给对象属性赋值

Season(String seasonName, String seasonDesc) {

this.seasonName = seasonName;

this.seasonDesc = seasonDesc;

}

// 3.提供当前枚举类的多个对象:public static final的

public static final Season SPRING = new Season("春天", "春天暖");

public static final Season SUMMER = new Season("夏天", "夏天热");

public static final Season AUTUMN = new Season("秋天", "秋天凉");

public static final Season WINTER = new Season("冬天", "冬天冷");

// 4.其他诉求1:获取枚举类对象的属性

public String getSeasonName() {

return seasonName;

}

public String getSeasonDesc() {

return seasonDesc;

}

// 4.其他诉求2:提供toString方法

@Override

public String toString() {

return "Season{" +

"seasonName='" + seasonName + '\'' +

", seasonDesc='" + seasonDesc + '\'' +

'}';

}

}

JDK5.0,使用enum关键字定义枚举类public class EnumClass {

public static void main(String[] args) {

spring = EnumClass1.SPRING;

System.out.println(spring.toString());

}

}

enum EnumClass1{

// 1.提供当前枚举类的对象,多个对象之间用逗号隔开

SPRING("春天", "温暖"),

SUMMER("夏天", "炎热"),

AUTUMN("秋天", "凉爽"),

WINTER("冬天", "寒冷");

private final String seasonName;

private final String seasonDesc;

EnumClass1(String seasonName, String seasonDesc) {

this.seasonName = seasonName;

this.seasonDesc = seasonDesc;

}

public String getSeasonName() {

return seasonName;

}

public String getSeasonDesc() {

return seasonDesc;

}

@Override

public String toString() {

return "EnumClass1{" +

"seasonName='" + seasonName + '\'' +

", seasonDesc='" + seasonDesc + '\'' +

'}';

}

}

常用方法toString()返回枚举类对象的名称sout(spring.toString); // 打印的是SPRINGvalues()返回所有的枚举类对象构成的数组EnumClass1[] values = EnumClass1.values();

for(int i = 0;i 

System.out.println(values[i]);

values[i].show();

}valueOf(String objName)返回枚举类中对象名是objName的对象EnumClass1 winter = EnumClass1.valueOf("WINTER");

//如果没有objName的枚举类对象,则抛异常:IllegalArgumentException

使用enum定义的枚举类实现接口情况一:实现接口,在enum类中实现抽象方法

情况二:让枚举类的对象分别实现接口中的抽象方法public class EnumClass {

public static void main(String[] args) {

EnumClass1 spring = EnumClass1.SPRING;

spring.show();

}

}

interface Info {

void show();

}

enum EnumClass1 implements Info{

// 1.提供当前枚举类的对象,多个对象之间用逗号隔开

SPRING("春天", "温暖") {

@Override

public void show() {

System.out.println("春暖花开");

}

},

SUMMER("夏天", "炎热"){

@Override

public void show() {

System.out.println("夏日炎炎");

}

},

AUTUMN("秋天", "凉爽"){

@Override

public void show() {

System.out.println("秋高气爽");

}

},

WINTER("冬天", "寒冷"){

@Override

public void show() {

System.out.println("冰天雪地");

}

};

private final String seasonName;

private final String seasonDesc;

EnumClass1(String seasonName, String seasonDesc) {

this.seasonName = seasonName;

this.seasonDesc = seasonDesc;

}

public String getSeasonName() {

return seasonName;

}

public String getSeasonDesc() {

return seasonDesc;

}

@Override

public String toString() {

return "EnumClass1{" +

"seasonName='" + seasonName + '\'' +

", seasonDesc='" + seasonDesc + '\'' +

'}';

}

// @Override

// public void show() {

//

// }

}

注解的使用

Annocation的使用示例示例一:生成文档相关的注解

示例二:在编译时进行格式检查(JDK内置的三个基本注解)@Override: 限定重写父类方法, 该注解只能用于方法

@Deprecated: 用于表示所修饰的元素(类, 方法等)已过时。通常是因为所修饰的结构危险或存在更好的选择

@SuppressWarnings: 抑制编译器警告

示例三:跟踪代码依赖性,实现替代配置文件功能

元注解修饰其他注解的注解

对现有的注解进行解释说明的注解通常都会有Retention和Target元注解Retention:指定所修饰的 Annotation 的生命周期:SOURCE \ CLASS(默认行为)\ RUNTIME

只有声明为RUNTIME生命周期的注解,才能通过反射获取。

Target:用于指定被修饰的 Annotation 能用于修饰哪些程序元素(可以用来修饰结构,例如修饰类、接口、枚举类、构造器等)Documented和Inherited出现的频率较低Documented:表示所修饰的注解在被javadoc解析时,保留下来。

Inherited:被它修饰的 Annotation 将具有继承性。集合、数组都是对多个数据进行存储操作的结构,简称Java容器。说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg,.avi,数据库中)

数组在存储方面的特点

特点一旦初始化以后,其长度就确定了。

数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了。比如:String[] arr;int[] arr1;Object[] arr2;

缺点一旦初始化以后,其长度就不可修改。

数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。

获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用

数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。

集合框架

Collection接口单列集合,用来存储一个一个的对象

List接口存储有序的,可重复的数据。(动态数组)通常使用List代替数组

ArrayListArrayList向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals().常用方法public class CollectionTest1 {

@Test

public void test1() {

Collection coll = new ArrayList();

// 1.add(Object e);将元素e添加到集合coll中

coll.add(123);

coll.add("abc");

coll.add('c');

coll.add(false);

coll.add(new Date());

// 2.size():获取添加的元素个数

System.out.println(coll.size()); // 4

// 3.addAll():将coll2中的元素添加到当前的集合coll中

Collection coll2 = new ArrayList();

coll2.add("***");

coll2.add("&&&");

coll.addAll(coll2);

System.out.println(coll.size()); // 6

System.out.println(coll);

// 4.clear():清空集合元素

// coll.clear();

// 5.isEmpty():判断集合是否为空,空为true

// System.out.println(coll.isEmpty());

// 6.contains(Object obj):判断当前集合中是否包含obj,包含为true

// 我们在判断时会调用obj对象所在类的equals()。自定义的类要重写equals()

// 向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals().

boolean contains1 = coll.contains(1234);

boolean contains2 = coll.contains(123);

System.out.println(contains1); // false

System.out.println(contains2); // true

// 7.containsAll(Collection coll):判断形参coll3中的所有元素是否都存在与当前集合中

Collection coll3 = Arrays.asList(123, "abc");

System.out.println(coll.containsAll(coll3)); // true

// 8.remove(Object obj):从当前集合中移除obj元素,不存在返回false,移除成功返回true

// 所以移除的元素也需要重写equals()

System.out.println(coll.remove(123)); // true

// 9.removeAll(Collection coll):从当前集合中移除coll4中所有的元素,只要有一个能够移除,那就就返回true

Collection coll4 = Arrays.asList("***", "&&");

System.out.println(coll.removeAll(coll4)); // true

// 10.retainAll(Collection coll):交集,获取coll和coll3集合的交集,并返回给coll(删掉不同的,保留一样的)

// System.out.println(coll);

// coll.retainAll(coll3);

// System.out.println(coll); // 将coll和coll3相同的返回到coll中

// 11.equals(Object obj):比较两个对象是否相等,每一个都得是一样的,ArrayList是有序的,所以比较的时候也要是有序的

Collection coll5 = new ArrayList();

coll5.add(123);

coll5.add('c');

System.out.println(coll.equals(coll5)); // false

// 12.hashCode():返回当前对象的哈希值

System.out.println(coll.hashCode());

// 13.toArray():集合 --> 数组的转换

Object[] arr = coll.toArray();

for (int i = 0; i 

System.out.println(arr[i]);

}

// 13拓展Arrays.asList(arr):数组 --> 集合的转换

List obj = Arrays.asList(arr);

System.out.println(obj);

}

}Iterator迭代器接口主要用于遍历Collection集合中的元素@Test

public void testIterator() {

Collection coll = new ArrayList();

coll.add(123);

coll.add(456);

coll.add("abc");

coll.add("def");

coll.add("***");

// iterator():返回Iterator接口的实例,用于遍历集合元素。放在IteratorTest.java中测试

Iterator iterator = coll.iterator();

// 方式一,不用

// System.out.println(iterator.next());

// System.out.println(iterator.next());

// 方式二,不用

// for (int i = 0; i 

//     System.out.println(iterator.next());

// }

// 方式三,推荐使用,hasNext()和next()搭配使用

while (iterator.hasNext()) { // 有元素就进入,没有退出

System.out.println(iterator.next());

}

iterator = coll.iterator();

// 删除集合中指定数据

while (iterator.hasNext()) {

Object obj = iterator.next(); // 迭代

if ("***".equals(obj)) {

iterator.remove();

}

}

// 重新遍历集合

iterator = coll.iterator();

while (iterator.hasNext()) {

System.out.println(iterator.next());

}

}foreach循环迭代 jdk5.0新增用于遍历集合、数组将集合、数组中的元素取出来赋给定义的局部变量@Test

public void testForeach() {

Collection coll = new ArrayList();

coll.add(123);

coll.add(456);

coll.add("abc");

coll.add("def");

coll.add("***");

// for(集合中元素的类型 局部变量 : 集合对象)

for (Object obj : coll) {

System.out.println(obj);

}

// for(数组中元素的类型 局部变量 : 数组对象)

int[] arr = new int[]{1,2,3,4,5};

for (int i : arr) {

System.out.println(i);

}

}

LinkedList

Vector

List接口常用方法总结增:add(Object obj)

删:remove(int index)(按照索引删除) / remove(Object obj)(按照对象删除)

改:set(int index, Object ele)

查:get(int index)

插:add(int index, Object ele)

长度:size()

遍历:Iterator、foreach

源码分析ArrayListjdk 7情况下ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData

list.add(123);//elementData[0] = new Integer(123);

...

list.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容。

默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity)jdk 8情况下ArrayList list = new ArrayList();//底层Object[] elementData初始化为{}.并没有创建长度为10的数组

list.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0]

...

后续的添加和扩容操作与jdk 7无异。小结:jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。LinkedList

LinkedList list = new LinkedList(); 内部声明了Node类型的first和last属性,默认值为null

list.add(123);//将123封装到Node中,创建了Node对象。

其中,Node定义为:体现了LinkedList的双向链表的说法private static class Node {

E item;

Node next;

Node prev;

Node(Node prev, E element, Node next)

{

this.item = element;

this.next = next;

this.prev = prev;

}

}Vector

jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。

在扩容方面,默认扩容为原来的数组长度的2倍。

面试题:ArrayList、LinkedList、Vector异同相同点三个类都实现了List接口,存储数据的特点相同:有序的,可重复的数据不同点ArrayList作为List接口的主要实现类(默认使用)

线程不安全的,效率高

底层使用Object[] elementData存储

LinkedList对于频繁的插入、删除操作,使用此类效率比ArrayList高

底层使用双向链表存储

Vector作为List接口古老实现类(jdk1.0),不会使用到

线程安全的,效率低

底层使用Object[] elementData存储

Set接口存储无序的,不可重复的数据。(数学中的集合->无序不重复)Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法。无序性:无序性不等于随机性(遍历的顺序还是按照添加的顺序打印)。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。

不可重复性:保证添加的元素按照equals()判断时,不能返回true.即:相同的元素只能添加一个。

HashSet、LinkedHashSet需要重写equals()、hashCode()

TreeSet需要重写Comparable:compareTo(Object obj)、Comparator:compare(Object o1,Object o2)

HashSet作为Set接口的主要实现类;线程不安全的;可以存储null值,底层用的是HashMapLinkedHashSet作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历。对于频繁的遍历操作,LinkedHashSet效率高于HashSet.LinkedHashSet是在HashSet的基础上,新增一个链表,在添加数据的同时,按顺序将数据先后用链表链接,记录此数据的前一个数据和后一个数据,提高遍历效率。

TreeSet可以按照添加对象的指定属性(例如指定int等),进行排序。

Map接口双列集合,用来存储一对(key, value)的数据。(数学中的函数->y=f(x), 映射不同的key可以对应多个value,多个value不能对应多个key

HashMap作为Map的主要实现类,线程不安全,效率高;存储null的key和valueLinkedHashMap保证在遍历map元素时,可以按照添加的顺序实现遍历

原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素

对于频繁的遍历操作,此类执行效率要高于HashMap

TreeMap保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或者定制排序

底层使用的是红黑树

Hashtable作为古老的实现类,线程安全,效率低;不能存储null的key和valueProperties常用来处理配置文件。key和value都是String类型

Map结构的理解Map中的key使用Set存储 ----> key所在的类要重写equals()和hashCode()

Map中的value使用Collection存储 ----> value所在的类要重写equals()

一个键值对(key-value)构成了一个Entry对象

Map常用方法put, putAll, remove, clear, size, get, containsKey, containsValue, isEmpty, equals@Test

public void test1() {

Map map = new HashMap();

// put 添加

map.put("AA", 123); // Key和Value各自的类型都是确定的(泛型)

map.put("BB", 456);

map.put("CC", 789);

// 修改

map.put("AA", 102);

Map map2 = new HashMap();

map2.put("AA", 103);

map2.put("DD", 1112);

map2.put("EE", 1314);

// putAll

map.putAll(map2);

// remove(Object obj)

Object value = map.remove("AA");

map.remove(value);

map.remove("CC");

// clear(),清空Map中的数据,但是Map不会为null

map.clear();

// size(),Map的大小

System.out.println(map.size());

System.out.println(map);

}

@Test

public void test2() {

Map map = new HashMap();

map.put("AA", 123);

map.put("BB", 456);

map.put("CC", 789);

// get(Object key),返回key的value值

System.out.println(map.get("AA"));

// containsKey(Object key),判断Map中是否有这个key

boolean isExist = map.containsKey("AA");

System.out.println(isExist);

// containValue(Object value),判断Map中是否有这个value

isExist = map.containsValue("123");

System.out.println(isExist);

// isEmpty(),判断Map是否为空

System.out.println(map.isEmpty());

Map map2 = new HashMap();

map2.putAll(map);

// equals(Object obj),判断当前Map和参数对象obj是否相等

System.out.println(map.equals(map2));

}遍历@Test

public void test3() {

Map map = new HashMap();

map.put("AA", 123);

map.put("BB", 456);

map.put("CC", 789);

// 遍历所有的key集:keySet(),key构成Set

Set set = map.keySet();

// 获得Set之后,再使用迭代器

Iterator iterator = set.iterator();

while (iterator.hasNext()) {

System.out.println(iterator.next());

}

// 遍历所有的value集:values()

Collection values = map.values();

// Iterator iterator1 = values.iterator();

// while (iterator1.hasNext()) {

//     System.out.println(iterator1.next());

// }

for (Object object : values) {

System.out.println(object);

}

// 遍历所有的key-value集:entrySet()

// 方式一:entrySet()

Set entrySet = map.entrySet();

Iterator iterator2 = entrySet.iterator();

while (iterator2.hasNext()) {

Object obj = iterator2.next();

// System.out.println(iterator2.next()); // 得到的是entry

Map.Entry entry = (Map.Entry)obj;

System.out.println(entry.getKey() + "--->" + entry.getValue());

}

// 方式二:先取得key,再用key得到value

}

面试题1、HashMap的底层实现原理(JDK 7.0)HashMap map = new HashMap();在实例化以后,底层创建了长度是16的一维数组Entry[] table

map.put(key1,value1);首先调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置。

如果此位置上的数据为空,此时的key1-value1添加成功。

如果此位置上的数据不为空 (意味着此位置上存在一个或多个数据,以链表形式存在),比较key1和已经存在的一个或多个数据的哈希值:

? 如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功

? 如果key1的哈希值与已经存在的某一个数据的哈希值相同,继续比较:调用key1所在类的equals()方法,比较:

? 如果equals()返回false:此时key1-value1添加成功

? 如果equals()返回true:使用value1替换相同key的value值扩容为原来的两倍,并将原来的数据复制过来

JDK 8.0相比较JDK7.0的不同:new HashMap():底层没有创建一个长度为16的数组

底层数组是:Node[],而非Entry[]

首次调用put()方法时,底层创建长度为16的数组

JDK7底层结构只有:数组 + 链表。JDK8底层结构有:数组 + 链表 + 红黑树当数组的某一个索引位置上的元素以链表形式存在的数据个数: > 8 , 且当前数组的长度:> 64 时。

此时该索引位置上的所有数据改为使用红黑树存储。2、HashMap和Hashtable的异同3、CurrentHashMap与Hashtable的异同

Collections工具类操作Collection、map工具类@Test

public void test1() {

List list = new ArrayList();

list.add(123);

list.add(1);

list.add(456);

list.add(43);

list.add(43);

System.out.println("原顺序:" + list);

// reverse(List):反转List中的元素顺序

Collections.reverse(list);

System.out.println("反转后的顺序:" + list);

// shuffle(List):对List中的元素进行随机排序

Collections.shuffle(list);

System.out.println("随机排序后:" + list);

// sort(List):根据元素的自然顺序对指定List集合元素按升序排序

// sort(List, Comparator):定制排序

// 按自然顺序排序,集合中的元素需要全是Integer类型

Collections.sort(list);

System.out.println("自然排序:" + list);

// swap(List, int, int):交换指定List集合中的i处元素和j处元素

Collections.swap(list, 1, 2);

System.out.println("交换后的排序" + list);

// max(Collection):根据元素的自然排序,返回给定集合中的最大元素

// max(Collection, Comparator), min 同理

System.out.println(Collections.max(list));

// frequency(Collection, Object):返回指定集合中指定元素的出现次数

System.out.println(Collections.frequency(list, 43));

// copy(List dest, List src):将src中的内容复制到dest中

//错误的写法,抛异常:IndexOutOfBoundsException: Source does not fit in dest

// List dest = new ArrayList();

// Collections.copy(dest, list);

List dest = Arrays.asList(new Object[list.size()]);

Collections.copy(dest, list);

System.out.println("复制后的dest" + dest);

// Collections中有多个synchronizedXxx()方法

// 该方法可将指定集合包装成线程同步的集合

// 从而解决多线程并发访问集合时的线程安全问题

// 返回的list1就是线程安全的

List list1 = Collections.synchronizedList(list);

}有泛型,都要用

4afa987734ecf4ed8ed007fbde039b36.png

bfe797773fbb507b490a3bebc3162afc.pngpublic class GenericTest {

/**

* @Author LiangHui

* @Description 测试没有泛型可能出现的问题

* @Date 2020/9/13 8:54

* @param

* @return void

*/

@Test

public void test1() {

ArrayList list = new ArrayList();

// 需求:存放学生成绩

list.add(78);

list.add(32);

list.add(45);

list.add(90);

// 问题一:类型不安全

// list.add("Tom");

for (Object score : list) {

// 问题二:强转出问题,ClassCastException

int stuScore = (Integer) score;

System.out.println(stuScore);

}

}

/**

* @Author LiangHui

* @Description 使用泛型

* @Date 2020/9/13 8:58

* @param

* @return void

*/

@Test

public void test2() {

ArrayList list = new ArrayList<>();

list.add(78);

list.add(32);

list.add(45);

list.add(90);

// 编译时就会进行类型检查,保证数据的安全

// list.add("Tom");

for (Integer score : list) {

// 避免了强转操作

int stuScore = score;

System.out.println(stuScore);

}

Iterator iterator = list.iterator();

while (iterator.hasNext()) {

System.out.println(iterator.next());

}

}

/**

* @Author LiangHui

* @Description 以HashMap为例

* @Date 2020/9/13 9:04

* @param

* @return void

*/

@Test

public void test3() {

Map map = new HashMap<>();

map.put("Tom", 90);

map.put("Jack", 80);

map.put("Kevin", 70);

map.put("Jerry", 100);

// 泛型的嵌套

Set> entry = map.entrySet();

Iterator> iterator = entry.iterator();

while (iterator.hasNext()) {

Map.Entry e = iterator.next();

String key = e.getKey();

Integer value = e.getValue();

System.out.println(key + "===>" + value);

}

}

/**

* @Author LiangHui

* @Description 自定义泛型的使用

* @Date 2020/9/13 9:53

* @param

* @return void

*/

@Test

public void test4() {

// 如果定义了泛型类,实例化没有指明类的方形,则认为此泛型类型为Object类型

// 要求:如果定义了类是带泛型的,建议在实例化时要指明类的泛型

OrderTest orderTest = new OrderTest();

orderTest.setOrderT(123);

orderTest.setOrderT("tse");

OrderTest order = new OrderTest<>();

order.setOrderT("Test");

}

}

/**

* @ClassName OrderTest

* @Description 自定义泛型

* @Author LiangHui

* @Date 2020/9/13 9:30

* @Version V1.0

*/

@Data // lombok

public class OrderTest {

private String orderName;

private int orderId;

// 类的内部结构就可以使用类的泛型

private T orderT;

}

通配符当类A是类B的父类,G和G是没有关系的不能相互赋值,二者的共同父类是G>,G和G都可以赋值给G>List list1 = null;

List list2 = null;

List> list = null; // 此时的>就是泛型的通配符,这样就将不同的泛型类型赋值给list了

list = list1;

list = list2;

// 使用

public void print(List> list){

Iterator> iterator = list.iterator();

while(iterator.hasNext()){

Object obj = iterator.next();

System.out.println(obj);

}

}

// list>不能添加数据,即list.add("a");会报错,只能添加null

// list>可以读取数据,读取到list2中的数据

sout(list.get(0));

有限制条件的通配符

61a50478695a38bc43719fcbd098d751.png

File类File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法。

并未涉及到写入或读取文件内容的操作。如果需要读取或写入文件内容,必须使用IO流来完成。

后续File类的对象常会作为参数传递到流的构造器中,指明读写的"终点"

File类的创建以及常用方法/**

* @Author LiangHui

* @Description 实例化File的三种方式

* @Date 2020/9/13 21:46

* @param

* @return void

*/

@Test

public void test1() {

// 构造器一:File(String filePath)

File file = new File("E:\\系统文件夹\\桌面\\hello.txt");

// 构造器二:FIle(String parentPath,String childPath)

File file2 = new File("E:\\系统文件夹", "桌面");

// 构造器三:File(String parentFile,String childPath)

File file3 = new File(file2, "hello.txt");

}

/**

* @Author LiangHui

* @Description File类的常用方法(仅操作文件,不包含文件内容)

* @Date 2020/9/13 22:13

* @param

* @return void

*/

@Test

public void test2() {

File file = new File("E:\\系统文件夹\\桌面\\hello.txt");

// getAbsolutePath:获取绝对路径

System.out.println(file.getAbsolutePath());

// getPath:获取路径

System.out.println(file.getPath());

// getName:获取名称

System.out.println(file.getName());

// getParent:获取上层文件目录路径,若无,返回null

System.out.println(file.getParent());

// length:获取文件长度(字节数),不能获取目录长度

System.out.println(file.length());

// lastModified:获取最后一次的修改时间,毫秒值

System.out.println(file.lastModified());

// String[] list():获取指定目录下的所有文件或者文件目录的名称数组

File file1 = new File("E:\\系统文件夹\\桌面");

String[] list = file1.list();

for (String s : list) {

System.out.println(s);

}

// File[] listFiles():获取指定目录下的所有文件或者文件目录的File数组

File[] files = file1.listFiles();

for (File f : files) {

System.out.println(f);

}

}

/**

* @Author LiangHui

* @Description file的重命名,把文件重命名为指定的文件路径,相当于移动

* @Date 2020/9/13 22:18

* @param

* @return void

*/

@Test

public void test3() {

File file1 = new File("E:\\系统文件夹\\桌面\\hello.txt");

File file2 = new File("test1.txt");

// 要想保证返回的true,需要file1在硬盘中存在,且file2不能在硬盘中存在

boolean renameTo = file1.renameTo(file2);

System.out.println(renameTo);

}

/**

* @Author LiangHui

* @Description file常用的判断方法

* @Date 2020/9/14 9:29

* @param

* @return void

*/

@Test

public void test4() {

File file = new File("test1.txt");

// isDirectory():判断是否是文件目录

System.out.println(file.isDirectory());

// isFile():判断是否是文件

System.out.println(file.isFile());

// exists():判断是否存在

System.out.println(file.exists());

// canRead():判断是否可读

System.out.println(file.canRead());

// canWrite():判断是否可写

System.out.println(file.canWrite());

// isHidden():判断是否隐藏

System.out.println(file.isHidden());

}

/**

* @Author LiangHui

* @Description 创建、删除硬盘中对应的文件或文件目录

* @Date 2020/9/14 9:36

* @param

* @return void

*/

@Test

public void test5() throws IOException {

File file1 = new File("test2.txt");

// 文件的创建和删除

if (!file1.exists()) {

file1.createNewFile(); // 若存在,则不创建,返回false

System.out.println("创建成功");

} else {

file1.delete(); // 文件和文件目录均可删除

System.out.println("删除成功");

}

File file2 = new File("mkdir");

// mkdir文件目录的创建,上层目录不存在,则不会创建

if (file2.mkdir()) {

System.out.println("文件目录创建成功");

} else {

file2.delete();

System.out.println("文件目录删除成功");

}

File file3 = new File("mkdir\\mkdirs");

// mkdirs如果创建的上一层目录是不存在的,则会一并创建

if (file3.mkdirs()) {

System.out.println("递归创建");

}

}

流的分类

5bdd30123c810980e3925803da3aa23c.png

e3b2c7cc8a517675ce144f3f28ff31af.png操作数据单位:字节流、字符流字节流处理01二进制数(图片音视频等),字符流处理文本(.txt等)

字节流处理:.jpg, .mp3, .mp4, .doc, .ppt等

字符流处理:.txt, .java, .c, .cpp等

数据的流向:输入流、输出流

流的角色:节点流、处理流

流的体系结构

944997637813e643394211509053fd35.png

抽象基类InputStream -> 字节输入流

OutputStream -> 字节输出流

Reader -> 字符输入流

Writer -> 字符输出流

节点流(或文件流)FileInputStream

FileOutputStream

FileReader

FIleWriter开发中不会单独使用这四个,因为效率差一点,一般还会使用缓冲流

处理字节流和处理字符流类似对于复制操作,使用字节流可以复制txt文本,但是如果txt文本有中文,使用字节流输入的话,可能会出现乱码。复制就没有影响

字符流不能复制字节流

处理字符流FileReader/**

* @Author LiangHui

* @Description FileReader字符节点输入流测试

* @Date 2020/9/14 13:57

* @param

* @return void

*/

@Test

public void testFileReader1() {

/* 不使用throws处理异常

因为当出现异常时,将异常抛出后,流不会关闭

所以适用try-catch来处理,一定要执行流的关闭操作

*/

FileReader fr = null;

try {

// 1、实例化File类的对象,指明要操作的文件,读入的文件一定要存在,否则报错

File file1 = new File("test1.txt");

// 2、提供具体的流

fr = new FileReader(file1);

// 3、数据的读入

// read():返回读入的一个字符。如果达到文件末尾,返回-1

int data;

while ((data = fr.read()) != -1) {

System.out.print((char) data);

}

} catch (IOException e) {

e.printStackTrace();

} finally {

try {

if (fr != null) { // 有可能fr没有实例化就抛出了异常,所以需要判断是否为null

// 4、流的关闭操作

fr.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

/**

* @Author LiangHui

* @Description FileReader字符节点输入流测试 2.0,使用read的重载方法

* @Date 2020/9/14 14:20

* @param

* @return void

*/

@Test

public void testFileReader2() {

FileReader fr = null;

try {

// 1、File类的实例化

File file1 = new File("test1.txt");

// 2、FileReader流的实例化(根据需要操作的文件来确定流)

fr = new FileReader(file1);

// 3、读入的操作(或者写出)

// read(char[] cbuf):返回每次读入cbuf数组中的字符个数,如果达到文件末尾,返回-1

char[] cbuf = new char[2];

int len;

while ((len = fr.read(cbuf)) != -1) {

// 方式一

// 错误的写法,最后一次未装满的还是保留着上一次的字符

// for (int i = 0; i 

//     System.out.println(cbuf[i]);

// }

// 正确的写法

// for (int i = 0; i 

//     System.out.println(cbuf[i]);

// }

// 方式二

String str = new String(cbuf, 0, len); // 添加条件

System.out.println(str);

}

} catch (IOException e) {

e.printStackTrace();

} finally {

try {

if (fr != null) {

// 4、资源的关闭

fr.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

}FileWriter/**

* @Author LiangHui

* @Description FileWriter字符节点输出流测试,从内存中写出数据到硬盘的文件里

* @Date 2020/9/14 14:53

* @param

* @return void

*/

@Test

public void testFileWriter1() {

FileWriter fw = null;

try {

// 1、File类的实例化

// 不存在的文件自动创建,已存在的文件覆盖写

// 要追加写,在实例化流的时候使用不同的构造器,加参数(true为追加),不加参数默认为false

File file1 = new File("test1.txt");

// 2、提供FileWriter的对象,用于数据的写出

fw = new FileWriter(file1);

// FileWriter fw = new FileWriter(file1, true); // 追击写

// 3、写出操作

fw.write("I have a dream!\n");

fw.write("you need to have a dream!");

} catch (IOException e) {

e.printStackTrace();

} finally {

if (fw != null) {

// 4、流资源的关闭

try {

fw.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

/**

* @Author LiangHui

* @Description 输入和写出结合,读出test1.txt文件中的数据,写入到test2.txt中

* @Date 2020/9/14 15:06

* @param

* @return void

*/

@Test

public void testFileReaderWriter() {

FileReader fileReader = null;

FileWriter fileWriter = null;

try {

// 1、创建File类的对象,指明读入和写入的文件

File srcFile = new File("test1.txt");

File destFile = new File("test2.txt");

// 2、创建输入流和输出流的对象

fileReader = new FileReader(srcFile);

fileWriter = new FileWriter(destFile);

// 3、数据的读入和写出操作

char[] cbuf = new char[5];

int len; // 记录每次读入到cbuf数组中的字符个数

while ((len = fileReader.read(cbuf)) != -1) {

fileWriter.write(cbuf, 0, len);

}

} catch (IOException e) {

e.printStackTrace();

} finally {

// 4、流资源关闭

try {

if (fileReader != null) {

fileReader.close();

}

} catch (IOException e) {

e.printStackTrace();

} // 将fileWriter.close() 写入到finally中也可以

try {

if (fileWriter != null) {

fileWriter.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

}

缓冲流(处理流中的一种)BufferedInputStream

BufferedOutputStream

BufferedReader

BufferedWriter开发中常用缓冲流,提高节点流的读取和写入速度

提高速度的原因:内部提供了一个缓冲区BufferedInputOutputStream/**

* @Author LiangHui

* @Description 复制非文本文件

* @Date 2020/9/14 17:20

* @param

* @return void

*/

@Test

public void testBufferedStream() {

BufferedInputStream bis = null;

BufferedOutputStream bos = null;

try {

// 1 造文件

File srcFile = new File("1989.png");

File destFile = new File("1989-2.png");

// 2 造流

// 2.1 造节点流

FileInputStream fis = new FileInputStream(srcFile);

FileOutputStream fos = new FileOutputStream(destFile);

// 2.2 造缓冲流

bis = new BufferedInputStream(fis);

bos = new BufferedOutputStream(fos);

// 3 复制的细节:读取、写入

byte[] buffer = new byte[10];

int len;

while ((len = bis.read(buffer)) != -1) {

bos.write(buffer, 0, len);

// 刷新缓冲区:缓冲区满了就清空

// bos.flush(); // 不用再写,调用的时候源码里有写

}

} catch (IOException e) {

e.printStackTrace();

} finally {

// 4 资源关闭:先关闭外层的流,再关闭内层的流

if (bos != null) {

try {

bos.close();

} catch (IOException e) {

e.printStackTrace();

}

}

if (bis != null) {

try {

bis.close();

} catch (IOException e) {

e.printStackTrace();

}

}

// 关闭外层流的同时,内层流也会自动关闭。所以内层流的关闭,可以省略

// fos.close();

// fis.close();

}

}BufferedReaderWriter/**

* @Author LiangHui

* @Description 使用BufferedReader和BufferedWriter实现文本复制

* @Date 2020/9/15 7:53

* @param

* @return void

*/

@Test

public void testBufferedReaderWriter() {

BufferedReader br = null;

BufferedWriter bw = null;

try {

// 将一二步整合到一步

br = new BufferedReader(new FileReader(new File("test1.txt")));

bw = new BufferedWriter(new FileWriter(new File("test3.txt")));

// 读写操作

// 方式一:使用char[]数组

// char[] cbuf = new char[1024];

// int len;

// while ((len = br.read(cbuf)) != -1) {

//     bw.write(cbuf, 0, len);

// }

// 方式二:使用String

String data;

while ((data = br.readLine()) != null){ // 一次读一行

// bw.write(data); // data中不包含换行符

// bw.write(data + "\n"); // 方式一:手动添加换行符

bw.write(data);

bw.newLine(); // 换行 方式二:使用newLine方法

}

} catch (IOException e) {

e.printStackTrace();

} finally {

if (br != null) {

try {

br.close();

} catch (IOException e) {

e.printStackTrace();

}

}

if (bw != null) {

try {

bw.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

总结抽象基类

InputStream

OutputStream

Reade

Writer

----------------------

节点流(或文件流)

FileInputStream (read(byte[] buffer))        FileOutputStream (write(byte[] buffer,0,len)  FileReader (read(char[] cbuf))                 FileWriter (write(char[] cbuf,0,len)

----------------------

缓冲流(处理流的一种)

BufferedInputStream (read(byte[] buffer))

BufferedOutputStream (write(byte[] buffer,0,len) / flush()

BufferedReader (read(char[] cbuf) / readLine())

BufferedWriter (write(char[] cbuf,0,len) / flush()FileInputStream fis = new FileInputStream(“hello.txt”);

在字节流中可以直接写路径(有这样的构造器)

转换流(处理流中的一种)转换流提供了再字节流和字符流之间的转换。

属于字符流InputStreamReader:将一个字节的输入流,转换为字符的输入流

OutputStreamWriter:将一个字符的输出流,转换为字节的输出流

226142cd008cd7d9da814325ed415de1.png/**

* @Author LiangHui

* @Description 使用转换流,将utf-8文件复制成gbk文件

* @Date 2020/9/15 14:37

* @param

* @return void

*/

@Test

public void test2() {

InputStreamReader isr = null;

OutputStreamWriter ows = null;

try {

// 1 创建file、字节流、转换流

File file1 = new File("test1.txt");

File file2 = new File("test4.txt");

FileInputStream fis = new FileInputStream(file1);

FileOutputStream fos = new FileOutputStream(file2);

isr = new InputStreamReader(fis, "utf-8");

ows = new OutputStreamWriter(fos, "gbk");

// 2 具体的写入写出操作

char[] cbuf = new char[20];

int len;

while ((len = isr.read(cbuf)) != -1) {

ows.write(cbuf, 0, len);

}

} catch (IOException e) {

e.printStackTrace();

} finally {

// 3 关闭流

if (ows != null ){

try {

isr.close();

} catch (IOException e) {

e.printStackTrace();

}

}

if (isr != null) {

try {

ows.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值