Java学习
常用的类
字符串相关的类
String
String新特性
String类:代表字符串,是一个final类,代表不可变的字符序列
String字符串是一个常量,使用双引号括起来,它们的值在创建之后不能改变。
String对象的字符内容是存储在一个字符数组Value[] 中的
String实例化
方式1:通过字面量的方式,这种方式声明的数据是放在方法区中
@Test
public void test2(){
String s1 = "abc";
String s2 = "abc";
System.out.println(s1==s2);
}
//执行结果true
方式2:通过new的方式,这种方式声明的数据是放在堆中
@Test
public void test2(){
String s3 = new String("hello");
String s4 = new String("hello");
System.out.println(s3==s4);
}
//执行结果false
String常用方法
int length():返回字符串的长度: return value.length
String str = "HelloWorld";
int len = str.length();
char charAt(int index): 返回某索引处的字符return value[index]
String str = "HelloWorld";
char c = str.charAt(2);
boolean isEmpty():判断是否是空字符串:return value.length == 0
String str = "HelloWorld";
boolean isEmpty = str.isEmpty();
String toLowerCase():使用默认语言环境,将 String 中的所有字符转换为小写
String str = "HelloWorld";
String lowStr = str.toLowerCase();
String toUpperCase():使用默认语言环境,将 String 中的所有字符转换为大写
String str = "HelloWorld";
String upperStr = str.toUpperCase();
String trim():返回字符串的副本,忽略前导空白和尾部空白
String str = " I Love You ";
String trimStr = str.trim();
boolean equals(Object obj):比较字符串的内容是否相同
String str1 = "I Love You";
String str2 = "I Love You";
boolean isEquals = str1.equals(str2);
boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写
String str1 = "I Love You";
String str2 = "i love you";
boolean isEquale = str1.equalsIgnoreCase(str2);
String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+”
String str1 = "Love You";
String str2 = "lyq";
String concatStr = str1.concat(str2);
int compareTo(String anotherString):比较两个字符串的大小
//返回的是ASCALL的差值
String str1 = "Hello";
String str2 = "Hella";
int i = str1.compareTo(str2);
String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
String str = "Hello";
String substring = str.substring(3);
System.out.println(substring);
String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
//这个区间是前闭红后开
String str = "Hello";
String substring = str.substring(3,4);
System.out.println(substring);
boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
String str = "Hello";
boolean b = str.endsWith("o");
boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始
String str = "Hello";
boolean h1 = str.startsWith("H");
boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
String str = "Hello";
boolean h1 = str.startsWith("H",1);
boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列时,返回 true
String str = "Hello";
boolean b = str.contains("o");
int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引
String str = "HelloHello";
int index = str.indexOf("e");
int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
String str = "HelloHello";
int index = str.indexOf("H", 4);
int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
//从右边开始找查
String str = "HelloHello";
int index = str.lastIndexOf("o");
int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
注:indexOf和lastIndexOf方法如果未找到都是返回-1
String str = "HelloHello";
int index = str.lastIndexOf("o",7);
String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
String str = "HelloWorld";
tring replace = str.replace("o", "x");
String 与基本数据类型,包装类之间的转换
String —》基本数据类型、包装类:调用包装类的静态方法parseXxx(字符串)
String str = "123";
int num = Integer.parseInt(str);
基本数据类型、包装类 —》 String:调用String重载的ValueOf
int num = "123";
String str = String.valueOf(num);
String与char[] 类型之间的数组转换 使用toCharArray()
String str = "abc123";
char[] chars = str.toCharArray();
for (char c:chars) {
System.out.println(c);
}
char[] 数组转换成String 直接使用String构造器里面穿一个char[] 数组
char[] arr = new char[]{'h','e','l','l','o'};
String str = new String(arr);
String与byte[]之间的转换
/**
* String 和 byte[] 之间的转换
* 编码:String --> byte[] 使用getBytes()或者getBytes(charasetName)
* 解码:byte[] --> String :使用String的构造器
*/
@Test
public void test3(){
//编码
String str = "hello李焕英";
byte[] bytes = str.getBytes();
System.out.println(Arrays.toString(bytes));
//解码
String s = new String(bytes);
System.out.println(s);
}
StringBuffer 和 StringBuilder
java.lang.StringBuffer代表可变的字符序列,JDK1.0中声明,可以对字符串内容进行增删,此时不会产生新的对象。
StringBuilder和StringBuffer类似。
StringBuffer 和 StringBuilder常用方法
StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接
StringBuffer sb = new StringBuffer("Love");
sb.append("you");
System.out.println(sb);
StringBuffer delete(int start,int end):删除指定位置的内容
StringBuffer sb = new StringBuffer("Love");
//左闭右开
sb.delete(0,2);
System.out.println(sb);
StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str
StringBuffer sb = new StringBuffer("Love");
sb.replace(0,2,"me");
System.out.println(sb);
StringBuffer insert(int offset, xxx):在指定位置插入xxx
//插入在指定位置的前面
StringBuffer sb = new StringBuffer("Love");
sb.insert(0,"haha");
System.out.println(sb);
StringBuffer reverse() :把当前字符序列逆转
StringBuffer sb = new StringBuffer("Love");
sb.reverse();
System.out.println(sb);
面试题目:
String s = new String(“abc”);这种方式创建对象,在内存中创建了几个对象?
两个,一个是堆中的new结构,另一个是char[] 对应的常量池中的数据 “abc”
public 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.print(ex.str + " and ");//goodand
System.out.println(ex.ch);//best
}
}
/*
为什么?首先change方法里面的两个参数传递的是引用,而String的特点是不可变的特性,所以对str并没有什么影响,而char[]就不一样了,引用会改变char[] 数组里面的内容
*/
String StringBuffer StringBuilder之间的异同?
String:不可变的字符序列,底层是使用char[]数组进行存储
StringBuffer:可变的字符序列,线程安全,底层是使用char[]数组进行存储
StringBuilder:可变的字符序列,线程不安全,底层是使用char[]数组进行存储
JDK8之前的API
- java.lang.System类
System类提供的public static long currentTimeMillis()用来返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差,此方法适于计算时间差。
@Test
public void test1(){
long longTime = System.currentTimeMillis();
System.out.println(longTime);
//执行结果 1614780990424
}
计算世界时间的主要标准有:
UTC(Coordinated Universal Time)
GMT(Greenwich Mean Time)
CST(Central Standard Time)
- java.util.Date类
构造器:
Date() 用来获取当前的系统时间
@Test
public void test2(){
Date data = new Date();
System.out.println(data);
//执行结果 Wed Mar 03 22:18:50 CST 2021 返回当前的时间
}
Date(long date) 传入一个的整型,来计算出时间
@Test
public void test2(){
Date data = new Date(1614780990424L);
System.out.println(data);
//执行结果 Wed Mar 03 22:16:30 CST 2021
}
常用方法
getTime():返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象
表示的毫秒数。
@Test
public void test2(){
Date data = new Date(1614780990424L);
long time = data.getTime();
System.out.println(time);
//执行结果:1614780990424
}
多线程
基本的概念,程序、进程、线程
程序:是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象
进程:是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。
线程:进程可进一步细化为线程,是一个程序内部的一条执行路径。
若一个进程同一时间并行执行多个线程,就是支持多线程的线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
一个进程中的多个线程共享相同的内存单元/内存地址空间它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。
线程的创建和使用
多线程的创建
方式1:
1、创建一个继承Thread的类
2、重写Thread类的run方法
3、创建Thread类的子类的对象
4、启动start方法
package com.niu.base;
/**
* 多线程的创建
* 方式1:
* 1、创建一个继承Thread的类
* 2、重写Thread类的run方法
* 3、创建Thread类的子类的对象
* 4、启动start方法
*/
class MyThread extends Thread{
@Override
public void run() {
for (int i=0;i<100;i++){
if (i%2==0){
System.out.println("线程"+i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
线程创建方式2,实现Runnable接口
1、创建一个实现了Runnable接口的类
2、实现类去实现Runnable中的抽象方法 run()
3、创建实现类的对象
4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5、通过Thread类的对象调用start()
package com.niu.base;
/**
* 线程创建方式2,实现Runnable接口
* 1、创建一个实现了Runnable接口的类
* 2、实现类去实现Runnable中的抽象方法 run()
* 3、创建实现类的对象
* 4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
* 5、通过Thread类的对象调用start()
*/
class MThread implements Runnable{
@Override
public void run() {
for (int i=0;i<100;i++){
if (i%2==0){
System.out.println(i);
}
}
}
}
public class MyThreadDemo {
public static void main(String[] args) {
MThread mThread = new MThread();
Thread t1 = new Thread(mThread);
t1.start();
}
}
thread匿名子类实现
package com.niu.base;
public class HiddenName {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
//要实现的方法
}
}.start();
new Thread(){
@Override
public void run() {
//要实现的方法
}
}.start();
}
}
Thread类的常用方法
void start(): 启动线程,并执行对象的run()方法
run(): 线程在被调度时执行的操作
String getName(): 返回线程的名称
**void setName(String name)😗*设置该线程名称
static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类
**static void yield():**线程让步暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
若队列中没有同优先级的线程,忽略此方法
**join() **:当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止低优先级的线程也可以获得执行
static void sleep(long millis):(指定时间:毫秒)令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。抛出InterruptedException异常
stop(): 强制线程生命期结束,不推荐使用
boolean isAlive():返回boolean,判断线程是否还活着
线程的优先级(1~10)
MAX_PRIORITY:10 最大优先级
MIN _PRIORITY:1 最小优先级
NORM_PRIORITY:5 默认优先级
**getPriority() :**返回线程优先值
**setPriority(int newPriority) :**改变线程的优先级
练习:创建两个线程,一个打印奇数,一个打印偶数
package com.niu.base.exer;
public class ThreadDemo {
public static void main(String[] args) {
MyThread1 myThread1 = new MyThread1();
MyThread2 myThread2 = new MyThread2();
myThread1.start();
myThread2.start();
}
}
class MyThread1 extends Thread{
@Override
public void run() {
for (int i=0;i<100;i++){
if (i%2!=0)
System.out.println("***********odd"+i);
}
}
}
class MyThread2 extends Thread{
@Override
public void run() {
for (int i=0;i<100;i++){
if (i%2==0){
System.out.println("------------over"+i);
}
}
}
}
线程的生命周期
新建: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源
运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态, run()方法定义了线程的操作和功能
阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中止自己的执行,进入阻塞状态
死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束
线程的同步
线程安全问题
问题出现的原因:某个操作没有完成时候,其他的线程参与进来所导致。
解决方案:加锁,或者同步机制
方式1:同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
/*
说明:
操作共享数据的代码,即需要被同步的代码
共享数据:多个线程共同操作的变量
同步监视器:锁,任何一个类的对象,都可以充当锁,要求多个线程要使用同一把锁
*/
解决Runnable方式的线程安全问题
案例代码:
package com.niu.base;
class MyRunnable implements Runnable{
private int ticket = 100;
Object obj = new Object();
@Override
public void run() {
synchronized(obj){
while(true){
if (ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"sale ticket,ticket ID is"+ticket);
ticket--;
}else{
break;
}
}
}
}
}
public class RunnableTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable);
Thread t2 = new Thread(myRunnable);
Thread t3 = new Thread(myRunnable);
t1.start();
t2.start();
t3.start();
}
}
解决继承方式的线程安全问题
案例代码
package com.niu.base;
class Window extends Thread{
private static int ticket = 100;
private static Object obj = new Object();
@Override
public void run() {
while(true){
synchronized(obj){
if (ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"sale ticket,ticket ID is"+ticket);
ticket--;
}else{
break;
}
}
}
}
}
public class WindowTicketTest {
public static void main(String[] args) {
Window t1 = new Window();
Window t2 = new Window();
Window t3 = new Window();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
方式2:使用同步方法解决线程安全问题
使用同步方法用来解决Runnable线程安全
package com.niu.base;
class RunnableMethod implements Runnable{
private int ticket=100;
@Override
public void run() {
while(true){
show();
}
}
public synchronized void show(){
if (ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"sale ticket,ticket ID is"+ticket);
ticket--;
}
}
}
public class RunnableMethodTest {
public static void main(String[] args) {
RunnableMethod rm = new RunnableMethod();
Thread t1 = new Thread(rm);
Thread t2 = new Thread(rm);
Thread t3 = new Thread(rm);
t1.start();
t2.start();
t3.start();
}
}
使用同步方法解决继承线程安全问题
package com.niu.base;
class Window1 extends Thread{
private static int ticket = 100;
@Override
public void run() {
while(true){
show();
}
}
public static synchronized void show(){
if (ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"sale ticket,ticket ID is"+ticket);
ticket--;
}
}
}
public class ExtendThreaedTest {
public static void main(String[] args) {
Window1 w1 = new Window1();
Window1 w2 = new Window1();
Window1 w3 = new Window1();
w1.start();
w2.start();
w3.start();
}
}
同步方法总结:
- 同步方法仍然使用到了同步监视器,我们是不需要显示的声明
- 非静态的同步方法,同步监视器是:this
静态同步方法,使用同步监视器是本身
局限性:操作时候,只能有一个线程参与,其他的线程等待,相当于是一个单线程,效率下降
好处:同步的方式,解决了线程安全问题
单例模式饿汉式线程安全问题
package com.niu.base;
public class BankTest {
}
class Bank{
private Bank(){}
private static Bank instance = null;
public static Bank getInstance(){
/**
* //方式1
* synchronized(Bank.class){
* if (instance == null){
* instance = new Bank();
* }
* return instance;
* }
*/
//方式2
if (instance == null){
synchronized (Bank.class){
if (instance == null){
instance = new Bank();
}
}
}
return instance;
}
}
死锁
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
锁(LOCK)
使用锁来解决线程安全问题
package com.niu.base;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
public static void main(String[] args) {
Window w = new Window();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Window implements Runnable{
private int ticket = 100;
//1.创建锁对象
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
try{
//2.调用锁定方法
lock.lock();
if (ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 售票,票号为 "+ticket);
ticket--;
}else{
break;
}
}finally{
//3.解锁
lock.unlock();
}
}
}
}
线程的通信
线程通信中的三个方法
wait():一旦执行这个方法,当前线程就会进入阻塞状态,并且释放同步监视器
notify():执行这个方法会唤醒被wait的一个线程,如果有多个线程被wait,则唤醒优先级高的
notifyAll():这个方法会唤醒所有被wait的线程
以上三个方法必须使用在同步代码块或者同步方法中,且调用者必须是同步代码块或同步方法中的同步监视器
class Number implements Runnable{
private int number = 1;
@Override
public void run() {
while(true){
synchronized (this){
notify();
if (number<=100){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"打印"+number);
number++;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
public class CommunicationTest {
public static void main(String[] args) {
Number num = new Number();
Thread t1 = new Thread(num);
Thread t2 = new Thread(num);
t1.setName("线程1:");
t2.setName("线程2:");
t1.start();
t2.start();
}
综合题目:
生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处
取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图
生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通
知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如
果店中有产品了再通知消费者来取走产品。
package com.niu.base;
/**
* 生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处
* 取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图
* 生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通
* 知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如
* 果店中有产品了再通知消费者来取走产品。
*/
class Clerk{
private int productCount = 0;
//生产
public synchronized void produceProduct() {
if (productCount<20){
productCount++;
System.out.println(Thread.currentThread().getName()+"开始生产第"+productCount+"个产品");
notify();
}else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费
public synchronized void consumeProduct(){
if (productCount>0){
System.out.println(Thread.currentThread().getName()+"开始消费第"+productCount+"个产品");
productCount--;
notify();
}else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Productor extends Thread{
private Clerk clerk;
public Productor(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始生产产品.......");
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.produceProduct();
}
}
}
class Customer extends Thread{
private Clerk clerk;
public Customer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始消费产品.....");
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.consumeProduct();
}
}
}
public class Demo {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor p1 = new Productor(clerk);
Customer c1 = new Customer(clerk);
p1.setName("生产者:");
c1.setName("消费者:");
p1.start();
c1.start();
}
}
JDK5.0新增线程创建方式
线程创建方式3
package com.niu.base;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 线程创建方式3、实现Callable接口。 jdk5.0新增
* 1、创建实现Callable接口的实现类
* 2、实现接口中的call()方法
* 3、创建Callable接口实现类的队形
* 4、将Callable接口实现类的对象传递到FutureTask构造器当中,创建FutureTask的对象
* 5、将FutureTask对象作为参数传递到Thread的构造器中,启动线程start()
*
*/
class Threads implements Callable{
@Override
public Object call() throws Exception {
int sum = 0;
for (int i=0;i<=100;i++){
if (i%2==0)
sum+=i;
}
return sum;
}
}
public class NumThread {
public static void main(String[] args) {
Threads threads = new Threads();
FutureTask futureTask = new FutureTask(threads);
new Thread(futureTask).start();
try {
Object sum = futureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
//创建线程方式4、使用线程池
package com.niu.base;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程创建方式4:使用线程池
* 1、提供指定线程数量的线程池
* 2、执行指定的线程的操作,需要提供实现Runnable接口或者Callable接口实现类的对象
* 3、关闭线程池
*
*/
class NumThreade implements Runnable{
@Override
public void run() {
for (int i=0;i<100;i++){
if (i%2==0)
System.out.println(i);
}
}
}
public class ThreadPool {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new NumThreade());
executorService.shutdown(); //关闭连接池
// executorService.submit();
}
}
面试题目:
说说synchronized与lock的异同?
相同:都可以用来解决线程安全问题
不同:synchronized机制在执行完相应的同步代码以后,自动释放同步监视器
lock需要手动的启动同步(调用lock())方法,同时结束同步也需要手动实现(unlock())
说说wait()和sleep()的异同?
相同:一旦执行,当前线程就会进入阻塞状态
不同:声明位置不同Thread类中声明sleep(),Object类中声明wait()
调用的要求不同sleep()在任何情况下都可以调用,wait()必须是在同步代码块或者同步方法中调用
关于是否释放同步监视器,如果两个方法都在同步代码块或者同步方法中使用sleep()不会释放锁,而wait()会释放锁
IO流部分
File类的使用
File 类的使用
1、File类的一个对象,代表一个文件或者一个文件目录
2、File类声明在java.io包下面
File类的构造方法
File类路径问题
相对路径
相对于本module
绝对路径
在磁盘上的路径
File类的常用方法
public String getAbsolutePath():获取绝对路径
File file1 = new File("hello.txt");
System.out.println(file1.getAbsolutePath());
public String getPath() :获取路径
File file1 = new File("hello.txt");
System.out.println(file1.getPath());
public String getName() :获取名称
File file1 = new File("hello.txt");
System.out.println(file1.getName());
public String getParent():获取上层文件目录路径。若无,返回null
File file1 = new File("hello.txt");
System.out.println(file1.getParent());
public long length() :获取文件长度(即:字节数)。不能获取目录的长度。
File file1 = new File("hello.txt");
System.out.println(file1.length());
public long lastModified() :获取最后一次的修改时间,毫秒值
File file1 = new File("hello.txt");
System.out.println(file1.lastModified());
public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组 相对路径
File file = new File("D:\\springBootstu\\javaBase");
String[] list = file.list();
for (String f:list) {
System.out.println(f);
}
public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组 绝对路径
File file = new File("D:\\springBootstu\\javaBase");
File[] files = file.listFiles();
for (File f:files){
System.out.println(f);
}
File类的重命名功能
public boolean renameTo(File dest):把文件重命名为指定的文件路径
File file1 = new File("hello.txt");
File file2 = new File("D:\\io\\hi.txt");
boolean renameTo = file1.renameTo(file2);
File类的判断功能
public boolean isDirectory():判断是否是文件目录
File file = new File("hello.txt");
System.out.println(file.isDirectory());
public boolean isFile() :判断是否是文件
File file = new File("hello.txt");
System.out.println(file.isFile());
public boolean exists() :判断是否存在
File file = new File("hello.txt");
System.out.println(file.exists());
public boolean canRead() :判断是否可读
File file = new File("hello.txt");
System.out.println(file.canRead());
public boolean canWrite() :判断是否可写
File file = new File("hello.txt");
System.out.println(file.canWrite());
public boolean isHidden() :判断是否隐藏
File file = new File("hello.txt");
System.out.println(file.isHidden());
File类的创建和删除
public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false
File file = new File("hi.txt");
file.createNewFile();
public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。
如果此文件目录的上层目录不存在,也不创建。
File file = new File("D:\\io\\nyc\\ll");
boolean mkdir = file.mkdir();
public boolean mkdirs() :创建文件目录。如果上层文件目录不存在,一并创建
File file = new File("D:\\io\\nyc\\ll");
boolean mkdir = file.mkdir();
public boolean delete():删除文件或者文件夹
File file = new File("hi.txt");
file.delete();
练习
- 利用File构造器,new 一个文件目录file
1)在其中创建多个文件和目录
2)编写方法,实现删除file中指定文件的操作
package com.niu.base.exer1;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
/**
* 1. 利用File构造器,new 一个文件目录file
* 1)在其中创建多个文件和目录
* 2)编写方法,实现删除file中指定文件的操作
*/
public class FileDemo {
@Test
public void test() throws IOException {
//利用File构造器,new 一个文件目录file
File file = new File("D:\\io\\niu1");
boolean mkdir = file.mkdirs();
System.out.println(mkdir);
//创建多个文件和目录
File file1 = new File("D:\\io\\nyc\\ll");
file1.mkdirs();
for (int i=0;i<100;i++){
File file2 = new File("D:\\io\\nyc\\ll\\"+i+".txt");
file2.createNewFile();
}
//编写方法,实现删除file中指定文件的操作
String[] list = file1.list();
for (String s:list){
if (s.equals("10.txt")){
File file4 = new File(file1.getPath()+"\\10.txt");
boolean delete = file4.delete();
System.out.println(delete);
}
}
}
}
- 判断指定目录下是否有后缀名为.jpg的文件,如果有,就输出该文件名称
package com.niu.base.exer1;
import org.junit.Test;
import java.io.File;
public class FileDemo1 {
@Test
public void test(){
//判断指定目录下是否有后缀名为.jpg的文件,如果有,就输出该文件名称
File file = new File("D:\\io\\nyc");
String[] list = file.list();
for (String s:list){
if (s.endsWith(".jpg")){
System.out.println(s);
}
}
}
}
IO流原理及流的分类
IO原理
I/O是Input/Output的缩写, I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等。
Java程序中,对于数据的输入/输出操作以“流(stream)” 的方式进行。
输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。
根据我们的思考角度不同,如果我们站在文件角度去思考就是输出,站在程序的角度去思考就是输入
流的分类
根据数据的单位不同分成:字节流(8bit),字符流(16bit)
按数据流的流向不同分为:输入流,输出流
按流的角色的不同分为:节点流,处理流
抽象基类 | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
输入流
将文件里面的内容读到程序里
package com.niu.base;
import org.junit.Test;
import java.io.File;
import java.io.FileReader;
public class FileReaderTest {
@Test
public void test() throws Exception {
//1、实例化File类的对象,指明要操作的文件
File file = new File("hello.txt");
//2、提供具体的流操作
FileReader fr = new FileReader(file);
int data;
//read()一次读入一个字符,到达文件的末尾 返回-1
//3、读入数据
while((data = fr.read())!=-1){
System.out.print((char) data);
}
//4、关闭流
fr.close();
}
}
对上面程序的优化写法
@Test
public void test2() throws Exception{
File file = new File("hello.txt");
FileReader fr = new FileReader(file);
int len;
char[] cbuf = new char[5];
//写法1
/**
* while((len=fr.read(cbuf))!=-1){
* for (int i=0;i<len;i++){
* System.out.print(cbuf[i]);
* }
* }
*/
//写法2
while((len=fr.read(cbuf))!=-1){
String str = new String(cbuf,0,len);
System.out.print(str);
}
}
输出流
从内存中写出数据到硬盘的文件里
@Test
//使用说明
/**
1.输出操作,对应的File文件可以不存在,在程序执行的时候会自己创建
2.在文件存在的情况下使用两种不同的参数会有两种结果
FileWriter fw = new FileWriter(file);/FileWriter fw = new FileWriter(file,false); 会进行文件的覆盖
FileWriter fw = new FileWriter(file,true);会进行文件的追加操作
*/
public void test3()throws Exception{
File file = new File("hello1.txt");
FileWriter fw = new FileWriter(file);
fw.write("I Love You !");
fw.close();
}
读入一个文件的内容,并将读到的内容在写出去(相当于一个复制操作)
@Test
public void test4() throws Exception{
File srcFile = new File("hello.txt");
File destFile = new File("hello2.txt");
FileReader fr = new FileReader(srcFile);
FileWriter fw = new FileWriter(destFile);
char[] cbuf = new char[5];
int len;
while((len = fr.read(cbuf))!=-1){
String str = new String(cbuf,0,len);
fw.write(str);
}
fr.close();
fw.close();
}
在处理图片、视频、音频…的情况下要使用字节流,处理文本的时候使用字符流即可
实现图片的复制操作(这个要使用字节流)
@Test
public void copyPicture() throws Exception{
File srcFile = new File("01.jpg");
File descFile = new File("test01.jpg");
FileInputStream fi = new FileInputStream(srcFile);
FileOutputStream fo = new FileOutputStream(descFile);
byte[] buffer = new byte[5];
int len;
while((len = fi.read(buffer))!=-1){
fo.write(buffer,0,len);
}
fi.close();
fo.close();
}
缓冲流
为了提高数据读写的速度
使用缓冲流完成复制操作
@Test
public void copyBuffer() throws Exception{
File srcFile = new File("01.jpg");
File destFile = new File("Test02.jpg");
FileInputStream fi = new FileInputStream(srcFile);
FileOutputStream fo = new FileOutputStream(destFile);
BufferedInputStream bfi = new BufferedInputStream(fi);
BufferedOutputStream bfo = new BufferedOutputStream(fo);
int len;
byte[] buffer = new byte[5];
while((len = bfi.read(buffer))!=-1){
bfo.write(buffer,len,0);
}
bfi.close();
bfo.close();
fi.close();
fo.close();
}
转换流
转换流的作用:提供字节流和字符流之间的转换
转换流属于字符流
@Test
public void test5() throws Exception{
FileInputStream fis = new FileInputStream("hello.txt");
InputStreamReader isr = new InputStreamReader(fis,"utf-8");
char[] cbuf = new char[10];
int len;
while((len = isr.read(cbuf))!=-1){
String str = new String(cbuf,0,len);
System.out.print(str);
}
isr.close();
fis.close();
}
综合使用InputStreamReader/OutPutStreamWriter编码格式的转换
@Test
public void test6() throws Exception{
FileInputStream fis = new FileInputStream(new File("hello.txt"));
FileOutputStream fos = new FileOutputStream(new File("hello_gbk.txt"));
InputStreamReader isr = new InputStreamReader(fis,"utf-8");
OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");
char[] cbuf=new char[10];
int len;
while((len = isr.read(cbuf))!=-1){
osw.write(cbuf,0,len);
}
isr.close();
osw.close();
}