递归读取文件中的内容
package com.easy723;
import java.io.File;
public class TestA {
public static void main(String[] args) {
printFileName(0,"D:\\111");
}
public static void printFileName(int num,String fileName){
File file =new File(fileName);
if(file.isDirectory()){
//打印自身的文件名
for(int i=0;i<num;i++){
System.out.print("-");
}
System.out.println("该文件夹的名字"+file.getPath());
//打印文件中的内容
File[] arr=file.listFiles();
for(File item:arr){
// System.out.println(item.getAbsolutePath());
printFileName(num+1,item.getAbsolutePath());
}
}
if(file.isFile()){
for(int i=0;i<num;i++){
System.out.print("-");
}
System.out.println(file.getPath());
}
}
}
序列化版本号
serialVersionUID是一个类的序列化版本
transient修饰一个属性防止被序列化
如果该变量没有定义,jdk会自动给一个版本号,当该类发生变化时,序列化版本号会发生变化,反序列化就会使失败
自定义该版本号,只要该版本号不发生变化,即使类中属性或者方法改变,该类的对象依旧可以反序列化
首先创建一个Student类定义属性和对应的get和set方法,toString方法,以及Constructor构造方法,
然后创建一个新的EasySerVersion类,对数据读入和写出
注意:(1)自定义版本号时需要连接接口implements Serializeble
(2)flush方法主要用于清除缓冲区的数据
Student
package com.easy724;
import java.io.Serializable;
public class Student implements Serializable {
public static final long serialVersionUID=1L;
private String name;
private String sex;
// private transient String sex;//transient修饰一个属性防止被序列化
private double score;
// public void study(){}
public Student(String name, String sex, double score) {
this.name = name;
this.sex = sex;
this.score = score;
}
public Student(){}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", score=" + score +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
}
读入
//读入
public static Student readStudent(){
File file=new File("D:\\student.data");
FileInputStream fis=null;
ObjectInputStream ois=null;
try{
fis=new FileInputStream(file);
ois=new ObjectInputStream(fis);
Object obj=ois.readObject();
if(obj instanceof Student){
return (Student) obj;
}
return null;
}catch(Exception e){
e.printStackTrace();
return null;
}finally {
if (ois!=null){
try{
ois.close();
}catch(IOException e){
e.printStackTrace();
}
}
if (fis!=null){
try{
fis.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
}
写出
//写出
public static void writeStudent(Student stu){
FileOutputStream fos=null;
ObjectOutputStream oos=null;
File file=new File("D:\\student.data");
if(!file.exists()){
try{
file.createNewFile();
}catch (IOException e){
e.printStackTrace();
}
}
try{
fos=new FileOutputStream("D:\\student.data");
oos=new ObjectOutputStream(fos);
oos.writeObject(stu);
oos.flush();//每一个输出流都有一个flush方法,flush对缓冲流起作用
}catch(IOException e){
e.printStackTrace();
}finally {
if (oos!=null){
try{
oos.close();
}catch(IOException e){
e.printStackTrace();
}
}
if (fos!=null){
try{
fos.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
}
}
线程 Thread
程序运行阶段的不同的运行路线
(1) .start开启线程
public static void main(String[] args) {
//实例化线程对象
Thread a=new ThreadA();//向上转型
Thread b=new ThreadA();
//开启线程
a.start();
b.start();//a,b可以同时交叉运行
// //主线程 普通对象调用方法
// a.run();
// b.run();//只能一个一个运行
}
}
(2)自定义线程用继承 extends
需要重写run方法来定义线程要执行的任务
//定义线程 继承
class ThreadA extends Thread{
//线程要执行的任务
//重写run方法来定义线程要执行的任务
@Override
public void run(){
for(int i=0;i<=20;i++){
System.out.println(i+this.getName());
}
}
}
2.线程常用的方法
(1)休眠的方法 sleep
sleep是一个Thread类的静态方法,休眠后会自动启动线程,
使用Thread.sleep(5000)表示让运行到该行代码的线程休眠5秒
首先自定义一个线程ThreadB,写一个1-20的数的循环,当遇到8的倍数时休眠2秒,再继续下一步。
class ThreadB extends Thread{
@Override
public void run() {
for(int i=0;i<20;i++){
if(i%8==0){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(i+this.getName());
}
}
}
然后定义threadsleep的方法来调用运行验证,第一个输出和第二个输出之间是否间隔5秒
//休眠的方法 sleep
public static void threadSleep() throws InterruptedException {
//sleep是一个Thread类的静态方法
System.out.println("1--------------");
Thread.sleep(5000);//让运行到该行代码的线程休眠 5秒
//休眠后会自动启动线程
System.out.println("2--------------");
}
运输结果
又 定义threadBSleep,用来输出线程ThreadB的运行结果,是运行后,先等待2秒,再输出循环的结果
public static void threadBSleep(){
Thread t=new ThreadB();
t.start();
}
(2)获取当前线程对象 Thread.currentThread()
//获取当前线程对象 Thread.currentThread()
public static void current(){
System.out.println(Thread.currentThread().getName());
}
(3)设置优先级 prioritty
注意:
// 优先级越高,线程在调度时获取CPU资源的概率理论上越大。
15 // Java线程的优先级范围是从1到10
16 // 默认情况下,线程的优先级是5。
17 // 如果尝试设置优先级为1到10之外的值,将会抛出IllegalArgumentException。
18
创建priority方法在里面创建a和b两个线程实例
并分别设置a,b线程的优先级别,最后启动a,b线程
//设置优先级
public static void priority(){
Thread a=new ThreadB();
Thread b=new ThreadB();
//设置优先级
a.setPriority(4);
b.setPriority(6);
//优先级越高,获取CPU资源的机率越大
//优先级从 1-10 默认是 5 设置1-10之外的值报错
a.start();
b.start();
}
(4)礼让 yeild
作用:让出cpu资源,让cpu重新分配,防止一条线程长时间占用资源,达到cpu资源合理分配的效果【还有sleep(0)也可以达到这样的效果】
首先自定义线程类ThreadC,用来写一个循环,并在出现3的倍数时执行礼让方法,然后定义threadYeild方法用来创建两个线程实例,并启动
class ThreadC extends Thread{
public void run() {
for(int i=0;i<20;i++){
if(i%3==0){
System.out.println(this.getName()+"--------执行了礼让方法");
Thread.yield();//礼让方法
}
System.out.println(i+this.getName());
}
}
}
//礼让 yeild
// 作用:让出CPU资源,让CPU重新分配,
// 防止一条线程长时间占用资源,达到CPU资源合理分配的效果
//还有sleep(0)也可以达到这样的效果
public static void threadYeild(){
ThreadC a=new ThreadC();
ThreadC b=new ThreadC();
a.start();
b.start();
}
在主函数中调用该方法运行结果如下,当出现3的倍数时执行礼让方法
(5)join() 成员方法 加入(插队)
在a线程中执行了b.join()方法 b线程运行完毕后,a线程再运行
定义一个线程类ThreadD,用来写join()方法的代码块,当达到类中所需条件时在b进程中断,并在后面插入a进程直到a进程结束,再继续b进程
class ThreadD extends Thread{
public ThreadD(Thread t){
this.t=t;
}
public ThreadD(){
}
private Thread t;
@Override
public void run() {
for(int i=0;i<=20;i++){
if(i==10&&t!=null&&t.isAlive()){
System.out.println(this.getName()+"----执行了JOIN");
try {
t.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println(i+this.getName());
}
}
}
/join() 成员方法 加入(插队)
//在A线程中执行了B.join() B线程运行完毕后,A线程再运行
public static void threadJoin(){
Thread a=new ThreadD();
Thread b=new ThreadD(a);
a.start();
b.start();
}
关闭线程
1.执行stop方法 不推荐使用
首先定义一个线程类ThreadE类,用来写一个1到100数字的循环,运行结果在半秒后开始
然后创建threadStop()方法,创建线程实例a,并启动线程,使用stop终止线程
class ThreadE extends Thread{
@Override
public void run() {
for(int i=0;i<=100;i++){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
}
}
}
//1.
public static void threadStop(){
Thread a=new ThreadE();
a.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
a.stop();
}
终止线程效果:
运行时,通过代码可以看到,主线程休眠两秒,a线程会在这2两秒内运行(每隔0.5秒运行一个)
2.设置中断状态,用interrupt(),这个线程不会中断,我们需要在线程内部判断,中断状态是否被设置,然后执行中断操作
threadInterrupted方法中首先启动线程a,如下面代码所示,主线程等待1毫秒,interrupt设置a为中断状态,然后在ThreadF类中判断循环是否是中断状态,如果是则跳出循环,终止进程
class ThreadF extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++){
if (Thread.currentThread().isInterrupted()){
break;
}
// try {
// Thread.sleep(500);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println(i);
}
}
}
//2.
public static void threadInterrupted(){
Thread a=new ThreadF();
a.start();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
a.interrupt();
}
3.自定义一个状态属性,在线程外部设置此属性,影响线程内部的运行
在方法stopThread中启动a线程,主线程停止一秒,ThreadG类中stop为false,则·! stop=true,循环结束,直到主线程休眠一秒结束,stop被设置为true,那么循环则停止,线程中断并打印出设置关闭
class ThreadG extends Thread{
volatile boolean stop=false;
@Override
public void run() {
while(!stop){
System.out.println("A");
}
}
}
//3.
public static void stopThread(){
ThreadG a=new ThreadG();
a.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
a.stop=true;
System.out.println("设置关闭");
}
线程同步:
线程安全
多个线程操作一个对象,不会出现结果错乱的情况,(StringBuffer线程安全)
线程不安全
StringBuilder是线程不安全,会出现数据丢失的情况
下面是StriingBuilder代码讲解:append方法是在现有字符的基础上添加新的字符或字符串
创建一个RunA类实现Runnable接口,在该类中构造函数接受一个StringBuilder实例,并实现run方法,在StringBuilder上追加字符“0”
在主方法中创建StringBUilder的对象strB,创建RunA的对象 r 并传递strB;创建第一个线程a并将对象r传递给Thread构造函数,创建第二个线程b,启动线程a 这也将执行RunA的run方法,启动线程b,也会执行RunA中的run方法;主线程休眠1秒,a线程和b线程在这段时间内执行,最后打印出两个线程总共追加了多少字符
package com.easy724.xiancheng;
//同步线程
public class SyncThreadA {
//线程安全
//多个线程操作一个对象,不会出现结果错乱的情况(缺失)
//StringBuffer线程安全
//线程不安全
//StringBuilder就是线程不安全,会出现数据丢失的情况
public static void main(String[] args) {
StringBuilder strB=new StringBuilder();
//runable线程可以执行的任务
RunA r=new RunA(strB);
Thread a=new Thread(r);
a.start();
Thread b=new Thread(r);
b.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(strB.length());
}
}
//实现Runable接口
class RunA implements Runnable{
StringBuilder strB;
public RunA(StringBuilder strB){
this.strB=strB;
}
@Override
public void run() {
for(int i=0;i<1000;i++){
strB.append("0");//append记住!!!!!
}
}
}
在这里得到的随机数据结果为1953,是因为StringBuilder是线程不安全的,所以有可能会出现数据丢失的情况,这里如果换成StringBuffer运行代码得到的结果总是为2000,因为它是线程安全的
synchrnoized
要做到线程安全·,我们可以使用synchronized,对方法或者代码块加锁,达到线程同步的效果
使用synchronized关键字修饰的方法或代码块,同一时间内,只允许一个线程执行此代码
public static void main(String[] args) {
Runnable r=new RunB();
Thread a=new Thread(r);
Thread b=new Thread(r);
a.start();
b.start();
}
(1)修饰方法 同步方法
//修饰方法 同步方法
public static synchronized void test(){
try {
System.out.println("-----进入方法"+Thread.currentThread().getName());
Thread.sleep(1000);
System.out.println("-----执行完毕"+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
(2)修饰代码块 同步代码块
//修饰代码块 同步代码块
public static void testA(){//静态方法
System.out.println("进入此方法"+Thread.currentThread().getName());
synchronized (SyncThreadB.class){//锁对象
System.out.println("进入同步代码块"+Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束同步代码块"+Thread.currentThread().getName());
}
}
class RunB implements Runnable{
@Override
public void run() {
SyncThreadB.testA();
}
}
锁对象 使用synchronized需要锁对象
synchronized修饰方法 成员方法 this 静态方法 类的类对象 obj.getClass() Easy.class
//锁的分类 //根据有无锁对象 分为悲观锁和乐观锁???定义?? 悲观锁有锁对象,乐观锁没有锁对象 //synchronized 悲观锁 //乐观锁实现方式 CAS和版本号控制???自己看???? //公平锁和非公平锁 公平锁就是先来后到 //java里面是非公平锁??? //可重入锁 在同步代码块中遇到相同的锁对象的同步代码块,不需要再获取锁对象的权限,直接进入执行 //根据线程状态不同分为 偏向锁,轻量级锁(自旋锁),重量级锁
线程生命周期
乐观锁实现方法 CAS和版本控制