异常机制
/**
* Throwable 类
* @author timevaeless
* @version 1.0
* @date 2020/8/23 2:41 AM
*/
public class AThrowable {
// ①java.lang.Throwable 类是 Error、Exception 的超类。
// Error - Java虚拟机无法解决的严重错误,通常无法编码解决,如:JVM挂掉了
// Exception - 因编程错误或偶然外在因素导致的轻微错误,通常可以编码解决,如:0作为除数等。
// ②java.lang.Exception
// - RuntimeException - 运行时异常,也叫非检测性异常
// ArithmeticException类 - 算术异常
// ArrayIndexOutOfBoundsException类 - 数组下标越界异常
// NullPointerException - 空指针异常
// ClassCastException - 类型转换异常
// NumberFormatException - 数字格式异常
// - 检测性异常,在编译阶段能被编译器检测出
// IOException
// InterruptException
//
//
public static void main(String[] args) {
// ③异常避免
// ArithmeticException
int a = 1;
int b = 0;
if (b != 0) {
System.out.println(a/b);
}
// ArrayIndexOutOfBoundsException
int[] arr = new int[5];
int pos = 5;
if (pos >= 0 && pos < arr.length) {
System.out.println(arr[pos]);
}
// NullPointerException
String str = null;
if (str != null) {
System.out.println(str.length());
}
// ClassCastException
Exception e = new Exception();
if (e instanceof IOException) {
IOException ioe = (IOException) e;
}
// NumberFormatException
str = "123a";
if (str.matches("\\d+")) {
Integer.parseInt(str);
}
// ④异常捕获
FileInputStream fis = null;
try {
fis = new FileInputStream("核心技术/src/cn/timevaeless/M异常/TThrowable.jav");
System.out.println("1");
} catch (IOException fileNotFoundException) {
fileNotFoundException.printStackTrace();
str = null;
// str.length();
System.out.println("2");
} finally {
try {
System.out.println("3");
fis.close();
} catch (IOException ioException) {
System.out.println("4");
ioException.printStackTrace();
} catch (NullPointerException nullPointerException) {
System.out.println("5");
nullPointerException.printStackTrace();
} catch (Exception exception) {
System.out.println("6");
// 最终异常必须在最后
}
}
System.out.println("Over."); // 如果catch中也发生异常了,还是会先执行finally里的方法,但程序不会往下执行了
// ⑤笔试考点
System.out.println(test());
}
public static int test() {
try {
int[] ints = new int[5];
int a = ints[ints.length];
return 1;
} catch (ArrayIndexOutOfBoundsException e) {
// 如果catch中有return,执行return前还是会先执行finally里的方法
return 2;
} finally {
return 3;
}
}
}
// ⑥子类重写不能抛出更大的异常,也不能抛出平级其他异常,可以抛出相同或更小的异常,也可以不抛出异常
class Person {
public void show() throws IOException {}
}
class Student extends Person {
@Override
public void show() /*throws Exception*/ /*throws ClassNotLoadedException*/ throws FileNotFoundException {}
}
/**
* 自定义异常
* @author timevaeless
* @version 1.0
* @date 2020/8/23 5:33 PM
*/
public class BUserdefinedException extends Exception {
// 自定义异常步骤
// ①定义一个序列化版本号,值随便写
// ②生成无参和有参构造方法
static final long serialVersionUID = 211111111113L; // 序列化版本号,与序列化操作有关
public BUserdefinedException() {
}
public BUserdefinedException(String message) {
super(message);
}
}
class Animal {
int age;
public Animal(int age) throws BUserdefinedException {
if (age < 0) {
throw new BUserdefinedException("年龄不合法");
}
this.age = age;
}
}
File类
/**
* File类
* @author timevaeless
* @version 1.0
* @date 2020/8/23 8:13 PM
*/
public class AFile {
// File类 - 用于获取文件或目录的特征信息,但不能修改
public static void main(String[] args) throws IOException {
// ①操作目录
File file = new File("核心技术/src/cn/timevaeless/N文件/A/B");
if (file.exists()) {
file.getName(); // N文件
file.length(); // 96B
file.getAbsolutePath(); // /Users/timevaeless/Documents/学习/未来/核心技术/src/cn/timevaeless/N文件
Date date = new Date(file.lastModified());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.format(date); // 2020-08-23 20:26:35 drwxr-xr-x 3 timevaeless staff 96B Aug 23 20:18 N文件
LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(file.lastModified()), ZoneId.systemDefault());
String format = localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // 2020-08-23 20:41:59
file.delete(); // false 要求目录为空, 只删除最后一个目录
} else {
file.mkdir(); // false 只能创建单层目录
file.mkdirs(); // true 可以创建多层目录
}
// ②操作目录
File file2 = new File("核心技术/src/cn/timevaeless/N文件/a.txt");
if (file2.exists()) {
file2.getName(); // a.txt
file2.length(); // 0
file2.getAbsolutePath(); // /Users/timevaeless/Documents/学习/未来/核心技术/src/cn/timevaeless/N文件/a.txt
LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(file2.lastModified()), ZoneId.systemDefault());
localDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); // 2020-08-23 20:51:42
file2.delete(); // true
} else {
file2.createNewFile(); // true 核心技术/src/cn/timevaeless/N文件/a.txt
}
// ③遍历目录
File file3 = new File("核心技术/src/cn/timevaeless");
for (File f : Objects.requireNonNull(file3.listFiles())) {
if (f.isDirectory()) {
System.out.println("> " + f);
} else {
System.out.println(f);
}
}
for (File f : Objects.requireNonNull(file3.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().matches("^.*Java.*");
}
}))) {
System.out.println(f);
}
// ④遍历所有子目录
File file4 = new File("核心技术/src/cn/timevaeless");
listFiles(file4);
}
private static void listFiles(File file) {
File[] files = file.listFiles();
if (files != null) {
for (File f: files) {
if (f.isDirectory()) {
System.out.println("|-- " + f);
listFiles(f);
} else {
System.out.println("|-- " + f);
}
}
}
}
}
IO流
/**
* IO流
*
* @author timevaeless
* @version 1.0
* @date 2020/8/23 10:44 PM
*/
public class AIOStream {
// ①IO - Input和Output
// 读写数据的基本单位不同 - 字节流(byte) 和 字符流(char)
// 字节流 - 以字节为单位进行数据读写的流,可以读写任意类型的文件
// 字符流 - 以字符(2个字节)为单位进行数据读写的流,只能读写文本文件
// 读写数据的方向不同 - 输入流 和 输出流
// 输入流 - 从文件中读取数据内容输入到程序中,也就是读文件
// 输出流 - 将程序中的数据内容输出到文件中,也就是写文件
// 流的角色不同 - 节点流 和 处理流
// 节点流 - 直接和输入输出源对接的流。
// 处理流 - 需要建立在节点流的基础之上的流
private static void fileWriterTest() {
FileWriter fileWriter = null;
try {
// 建立与文件的水管, true表示追加
fileWriter = new FileWriter("核心技术/src/cn/timevaeless/OIO流/b.txt", true);
// 放水
fileWriter.write("你好, IO!");
fileWriter.write('a');
fileWriter.write(65);
char[] chars = {'h', 'e', 'l', 'l', 'o'};
fileWriter.write(chars, 0, chars.length);
// 将水管中的水排干净
fileWriter.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileWriter != null) {
try {
// 抽走水管,自带刷新
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static void fileReaderTest() {
FileReader reader = null;
try {
// 水管插到文件中
reader = new FileReader("核心技术/src/cn/timevaeless/OIO流/b.txt");
// 抽水
int ret;
// 将文件里的字符一个一个读取出来
while ((ret = reader.read()) != -1) {
System.out.println((char) ret);
}
char[] chars = new char[2];
// 每次读取2个字符
while ((ret = reader.read(chars)) != -1) {
System.out.println("字符个数是 " + ret);
System.out.println(Arrays.toString(chars));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 字符流拷贝,只能拷贝文本,不能拷贝字节
*/
private static void fileCopy() {
FileReader reader = null;
FileWriter writer = null;
try {
reader = new FileReader("核心技术/src/cn/timevaeless/OIO流/b.txt");
writer = new FileWriter("核心技术/src/cn/timevaeless/OIO流/a.txt");
int len;
char[] chars = new char[2];
while ((len = reader.read(chars)) != -1) {
writer.write(chars, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void fileCopy2() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("核心技术/src/cn/timevaeless/OIO流/01.png");
fos = new FileOutputStream("核心技术/src/cn/timevaeless/OIO流/02.png");
fis.available(); // 文件大小
int len;
byte[] bytes = new byte[2];
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static void fileCopy3() {
FileInputStream fis = null;
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
fis = new FileInputStream("核心技术/src/cn/timevaeless/OIO流/01.png");
fos = new FileOutputStream("核心技术/src/cn/timevaeless/OIO流/02.png");
bis = new BufferedInputStream(fis, 8192); // size指定缓冲区大小
bos = new BufferedOutputStream(fos, 1024);
// 自己再加层缓冲
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (bos != null) {
bos.close(); // 会顺带把fos关闭
}
if (bis != null) {
bis.close(); // 会顺带把fis关闭
}
//fos.close();
//fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void fileCopy4() throws IOException {
BufferedReader reader = new BufferedReader(new FileReader("核心技术/src/cn/timevaeless/OIO流/b.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("核心技术/src/cn/timevaeless/OIO流/a.txt"));
//int len;
//char[] chars = new char[1024];
//while ((len = reader.read(chars)) != -1) {
// writer.write(chars, 0, len);
//}
String lineContent;
while ((lineContent = reader.readLine()) != null) {
writer.write(lineContent);
}
writer.close();
reader.close();
}
private static void printStreamTest() throws IOException {
// 从键盘输入,需要System.in,但是System.in是InputStream字节流,BufferedReader需要传入字符流
// 所以可以使用转换流来将字节流转换为字符流
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
// PrintStream printStream = new PrintStream(new FileOutputStream("核心技术/src/cn/timevaeless/OIO流/b.txt"));
PrintWriter printWriter = new PrintWriter(new FileWriter("核心技术/src/cn/timevaeless/OIO流/b.txt"));
String content;
while (!"Bye".equals(content = reader.readLine())) {
printWriter.println(content);
}
printWriter.close();
reader.close();
}
private static void dataOutputStreamTest() throws IOException {
DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("核心技术/src/cn/timevaeless/OIO流/b.txt"));
// 优先写入高字节,所以输出为 空格空格空格A
dataOutputStream.writeInt(65);
dataOutputStream.close();
}
private static void objectOutputStreamTest() throws IOException, ClassNotFoundException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("核心技术/src/cn/timevaeless/OIO流/b.txt"));
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("核心技术/src/cn/timevaeless/OIO流/b.txt"));
objectOutputStream.writeObject(new Person());
// readObject 方法没办法判断是不是最后一个对象,所以当希望将多个Object写入到文件时,可以把对象放入到集合中,然后写入这个集合到输出流
Object object = objectInputStream.readObject();
if (object instanceof Person) {
System.out.println(((Person)object).getAge());
}
objectOutputStream.close();
objectInputStream.close();
}
private static void randomAccessFileTest() throws IOException {
// r
// rw
// rwd - 支持同步
// rws - 支持同步
RandomAccessFile accessFile = new RandomAccessFile("核心技术/src/cn/timevaeless/OIO流/a.txt", "r");
int len;
byte[] bytes = new byte[2];
while ((len = accessFile.read(bytes)) != -1) {
System.out.println("读到的内容是:" + Arrays.toString(bytes));
}
accessFile.seek(0);
System.out.println((char)accessFile.read());
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
// ②FileWriter
AIOStream.fileWriterTest();
// ③FileReader
AIOStream.fileReaderTest();
AIOStream.fileCopy();
// ④FileInputStream && FileOutputStream 拷贝二进制
AIOStream.fileCopy2();
// ⑤BufferedInputStream && BufferedOutputStream 缓冲字节流 不用读写每个字节都与底层操作系统交互
AIOStream.fileCopy3();
// ⑤BufferedReader && BufferedWriter 缓冲字符流
AIOStream.fileCopy4();
// ⑥PrintStream && PrintWriter
// AIOStream.printStreamTest();
// ⑦字符编码
// ASCII: 美国人使用的文字和整数之间的关系,用一个字节的低7位表示
// ISO8859-1: 欧洲人使用的文字和整数之间的关系,用一个字节表示
// GB2312: 中国人使用的文字和整数之间的关系,英文用一个字节表示,汉字用两个字节表示
// GBK: GB2312的升级,融合了更多的中文,英文用一个字节表示,汉字用两个字节表示
// Unicode: 国际标准码,融合了全球所有的字,用两个字节表示
// UTF-8: Unicode只是定义了映射关系,没定义怎么存储,UTF-8就是实现了怎么存储,用1-4字节来表示一个字符,1 - 0,2 - 110,3 - 1110
// ⑧DataOutputStream && DataInputStream - 将基本数据类型写入输出流或从输入流读出
AIOStream.dataOutputStreamTest();
// ⑧ObjectOutputStream && ObjectInputStream
AIOStream.objectOutputStreamTest();
// ⑨RandomAccessFile - 随机访问文件,可以通过seek来跳
AIOStream.randomAccessFileTest();
}
}
class Person implements Serializable {
// 序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的
// 在进行反序列化时,JVM会把传入的字节流的serialVersionUID与对应的实体类的serialVersionUID进行比较
// 如果不一致,则抛出InvalidCastException
private static final long serialVersionUID = 4118104687707525866L;
private int age;
// transient 表示该成员变量不参与序列化
private transient int password;
public int getAge() {
return age;
}
}
多线程
新建状态 - 使用new关键字创建之后进入的状态,此时线程并没有开始执行。
就绪状态 - 调用start方法后进入的状态,此时线程还是没有开始执行。
运行状态 - 使用线程调度器调用该线程后进入的状态,此时线程开始执行,当线程的时间片执行完毕后任务没有完成时回到就绪状态。
消亡状态 - 当线程的任务执行完成后进入的状态,此时线程已经终止。
阻塞状态 - 当线程执行的过程中发生了阻塞事件进入的状态,如:sleep方法。
阻塞状态解除后进入就绪状态。
在 JVM 运行中,线程一共有 NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED 六种状态,这些状态对应 Thread.State 枚举类中的状态。
当创建一个线程时,线程处在 NEW 状态,运行 Thread 的 start 方法后,线程进入 RUNNABLE 可运行状态。
这时,所有可运行状态的线程并不能马上运行,而是需要先进入就绪状态等待线程调度,如图中间所示的 READY 状态。在获取 CPU 后才能进入运行状态,如图中所示的 RUNNING。运行状态可以随着不同条件转换成除 NEW 以外的其他状态。
如图左侧所示,在运行态中的线程进入 synchronized 同步块或者同步方法时,如果获取锁失败,则会进入到 BLOCKED 状态。当获取到锁后,会从 BLOCKED 状态恢复到就绪状态。
如图右侧所示,运行中的线程还会进入等待状态,这两个等待一个是有超时时间的等待,例如调用 Object.wait、Thread.join 等;另外一个是无超时的等待,例如调用 Thread.join 或者 Locksupport.park 等。这两种等待都可以通过 notify 或 unpark 结束等待状态并恢复到就绪状态。
最后是线程运行完成结束时,如图下侧所示,线程状态变成 TERMINATED。
/** * @author timevaeless * @version 1.0 * @date 2020/8/24 5:46 PM */ public class AThread { // ①简述线程、程序、进程的基本概念。以及他们之间关系是什么? // 程序 - 可执行文件 // 进程 - 运行中的程序,系统运行程序的基本单位 // 线程 - 一个进程在其执行的过程中可以产生多个线程 // // - 线程共享所在进程的系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程 // - 多线程是采用时间片轮转法来保证多个线程的并发执行,所谓并发就是指宏观并行微观串行的机制。 public static void threadTest() { // Thread() // Thread(String name) // Thread(Runnable target) // Thread(Runnable target, String name) // void run() - 若使用Runnable,调用该方法时最终调用接口中的版本,若没有使用Runnable构造线程对象,调用该方法时则啥也不做 // @Override // public void run() { // if (target != null) { // target.run(); // } // } // void start() - 用于启动线程,Java虚拟机会自动调用该线程的run方法 // long getId() - 获取调用对象所表示线程的编号 // String getName() - 获取调用对象所表示线程的名称 // void setName(String name) - 设置/修改线程的名称为参数指定的数值 // static Thread currentThread() - 获取当前正在执行线程的引用 Thread thread = new Thread(); thread.run(); // 啥也不做 Thread thread2 = new SubThread(); thread2.run(); // 相当于普通方法的调用 thread2.start(); // 启动子线程执行 } public static void runnableTest() { SubRunnable runnable = new SubRunnable(); Thread thread = new Thread(runnable); thread.start(); // 匿名形式 new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ": Hello InnerClass!"); } }).start(); } private static void sleepTest() { new Thread(() -> { int cnt = 0; while (cnt < 2) { String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); System.out.println(date); try { Thread.sleep(1000L); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + ": 被打乱了!"); } cnt++; } }).start(); } private static void priorityTest() { new Thread(() -> { Thread currentThread = Thread.currentThread(); currentThread.setPriority(Thread.MAX_PRIORITY); System.out.println(Thread.currentThread().getName() + ": " + currentThread.getPriority()); for (int i = 0; i < 20; i++) { System.out.println(String.format("%s, Priority: %s, %s", currentThread.getName(), currentThread.getPriority(), i)); } }).start(); } private static Thread joinTest() { Thread thread = new Thread(() -> { System.out.println("乖乖等我执行完..."); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } }); thread.start(); return thread; } private static Thread daemonTest() { Thread thread = new Thread(() -> { System.out.println("我是守护线程吗?" + Thread.currentThread().isDaemon()); try { System.out.println("如果我是守护线程,主线程是不会等我拉完屎的!"); Thread.sleep(10000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("我拉完啦!"); }); return thread; } public static void main(String[] args) throws InterruptedException { // ①通过继承Thread类创建线程 AThread.threadTest(); // ②通过实现Runnable接口创建线程 AThread.runnableTest(); // ③线程的生命周期 // 新建状态,new Thread()之后进入的状态 Thread.State state = Thread.State.NEW; // 可运行状态,分为Ready和Running。thread.start()之后进入的状态 // Ready状态的线程由线程调度器调度后变为Running状态 // 当线程的时间片用完后变为Ready状态 state = Thread.State.RUNNABLE; // 阻塞状态,当线程获取锁失败时会进入此状态等待锁释放, state = Thread.State.BLOCKED; // 无超时的等待,当线程调用Object.wait()或Thread.join()后会进入此状态,需要通过Object.notify()恢复到Ready状态 state = Thread.State.WAITING; // 有超时的等待,当线程调用Object.wait(long)或Thread.join(long)或Thread.sleep(long)后会进入此状态,超时或调用Object.notify()都能恢复到Ready状态 state = Thread.State.TIMED_WAITING; state = Thread.State.TERMINATED; // ④static void yield() - 当前线程让出处理器(离开Running状态),使当前线程进入Runnable状态等待 // ⑤static void sleep(times) - 使当前线程从 Running 放弃处理器进入Block状态, 休眠times毫秒, 再返回到Runnable, // 如果其他线程打断当前线程的Block(sleep), 就会发生InterruptedException, 不会释放锁。 AThread.sleepTest(); // ⑥int getPriority() - 获取线程的优先级,优先级越高的线程不一定先执行,但该线程获取到时间片的机会会更多一些 // MAX_PRIORITY 10 // MIN_PRIORITY 1 // NORM_PRIORITY 5 AThread.priorityTest(); Thread currentThread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + ": " + currentThread.getPriority()); for (int i = 0; i < 20; i++) { System.out.println(String.format("%s, Priority: %s, %s", currentThread.getName(), currentThread.getPriority(), i)); } // ⑦void join() - 等待该线程终止 Thread t = joinTest(); t.join(1000); // 最多等你1秒 System.out.println("不等。"); // ⑧守护线程 - 主线程结束了,守护线程会立即结束 // boolean isDaemon() - 判断是否为守护线程 // void setDaemon(boolean on) - 用于设置线程为守护线程 t = daemonTest(); t.setDaemon(true); t.start(); System.out.println(Thread.currentThread().getName() + ": Over."); } } class SubThread extends Thread { @Override public void run() { System.out.println("子线程的编号是:" + getId()); System.out.println( getName() + ": Hello Thread!"); } } class SubRunnable implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + ": Hello Runnable!"); } }
同步锁
/**
* @author timevaeless
* @version 1.0
* @date 2020/8/25 3:40 PM
*/
public class BSynchronized implements Runnable {
private static final Object lock = new Object();
private int balance = 1000;
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public /*synchronized*/ void run() {
// 取200块
String name = Thread.currentThread().getName();
System.out.println(name + " 来取200块");
// synchronized相当于厕所门,如果不加,两个人会蹲在同一个坑位...
synchronized (lock) {
if (balance >= 200) {
System.out.println("出钞中...");
try {
Thread.sleep(2000); // 呼呼大睡的时候,其他线程也执行到这了,balance的值并没有被改
} catch (InterruptedException e) {
e.printStackTrace();
}
setBalance(balance - 200);
} else {
System.out.println("余额不足.");
}
}
}
public static void main(String[] args) {
// ①线程为什么需要同步机制?
// 当多个线程同时访问同一种共享资源时,可能会造成数据的覆盖等不一致性问题,此时就需要对线程之间进行通信和协调,该机制就叫做线程的同步机制。
// 使用synchronized关键字来实现同步/对象锁机制从而保证线程执行的原子性
// ②synchronized如果加在成员方法上,等价于sychronized(this),如果加在静态方法上,等价于sychronized(类名.class)
BSynchronized account = new BSynchronized();
Thread t1 = new Thread(account);
Thread t2 = new Thread(account);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("余额: " + account.getBalance());
System.out.println("Over.");
// ③StringBuffer之所以和StringBuilder相比是线程安全的,就是几乎每个方法上都加了synchronized关键字修饰
// Vector和HashTable之所以和HashMap相比是线程安全的,就是几乎每个方法上都加了synchronized关键字修饰
// 但实际开发中,多线程场景下依然不使用Vector和HashTable,而是使用Collections工具类下的同步方法
HashMap<String, String> map = new HashMap<>();
Map<String, String> synchronizedMap = Collections.synchronizedMap(map);
}
}
死锁
/**
* @author timevaeless
* @version 1.0
* @date 2020/8/25 5:02 PM
*/
public class CDeadLock {
public static void main(String[] args) {
// ①产生的原因:使用了嵌套的synchronized代码块
new Thread(new A()).start();
new Thread(new B()).start();
}
}
class A implements Runnable {
@Override
public void run() {
synchronized (A.class) {
System.out.println("A拿到A锁了!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B.class) {
System.out.println("A拿到B锁了!");
}
}
}
}
class B implements Runnable {
@Override
public void run() {
synchronized (B.class) {
System.out.println("B拿到B锁了!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (A.class) {
System.out.println("B拿到A锁了!");
}
}
}
}
Lock
/**
* @author timevaeless
* @version 1.0
* @date 2020/8/25 7:08 PM
*/
public class DLock implements Runnable {
private int money;
private ReentrantLock lock;
public DLock(int money) {
this.money = money;
lock = new ReentrantLock();
}
@Override
public void run() {
lock.lock();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
money = money - 1;
lock.unlock();
}
System.out.println("money = " + money);
}
public static void main(String[] args) {
// ①Lock是显式锁,需要手动实现开启和关闭操作,而synchronized是隐式锁,执行锁定代码后自动释放。
// Lock只有同步代码块方式的锁,而synchronized有同步代码块方式和同步方法两种锁。
// 使用Lock锁方式时,Java虚拟机将花费较少的时间来调度线程,因此性能更好。
DLock runnable = new DLock(2000);
new Thread(runnable).start();
new Thread(runnable).start();
}
}
Wait && Notify
/**
* @author timevaeless
* @version 1.0
* @date 2020/8/25 8:12 PM
*/
public class EWaitAndNotify implements Runnable {
private int cnt = 0;
@Override
public void run() {
while (true) {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + "大喊一声:我干完这票就不要锁啦!!!");
notify();
if (cnt <= 100) {
System.out.println(Thread.currentThread().getName() + ": cnt = " + cnt);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
cnt++;
try {
System.out.println(Thread.currentThread().getName() + ": 我释放锁,进入WAIT状态啦!!!");
wait(); //
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
public static void main(String[] args) {
// ①线程间通信
// wait() - 让线程进入阻塞状态,并自动释放锁,所以必须在锁定的代码中调用
EWaitAndNotify runnable = new EWaitAndNotify();
new Thread(runnable).start();
new Thread(runnable).start();
}
}
生产者与消费者①
/**
* @author timevaeless
* @version 1.0
* @date 2020/8/25 8:34 PM
*/
public class FProducerConsumer {
public static void main(String[] args) {
StoreHouse storeHouse = new StoreHouse();
Producer producer = new Producer(storeHouse);
Consumer consumer = new Consumer(storeHouse);
new Thread(producer).start();
new Thread(consumer).start();
}
}
class Producer implements Runnable {
private StoreHouse storeHouse;
public Producer(StoreHouse storeHouse) {
this.storeHouse = storeHouse;
}
@Override
public void run() {
while (true) {
storeHouse.produce();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
private StoreHouse storeHouse;
public Consumer(StoreHouse storeHouse) {
this.storeHouse = storeHouse;
}
@Override
public void run() {
while (true) {
storeHouse.consume();
// 上面的方法会抢占锁资源,所以要暂停一下
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Bao {
int price;
}
class StoreHouse {
private List<Bao> baoList;
public StoreHouse() {
this.baoList = new ArrayList<>();
}
public synchronized void produce() {
// 进来必喊一嗓子
notify();
if (baoList.size() < 10) {
baoList.add(new Bao());
System.out.println(Thread.currentThread().getName() + "生产了一个包");
} else {
// 歇会
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void consume() {
// 进来必喊一嗓子
notify();
if (baoList.size() <= 0) {
// 歇会
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
baoList.remove(0);
System.out.println(Thread.currentThread().getName() + "卖出去一个包!");
}
}
}
生产者消费者模式②
/**
* @author timevaeless
* @version 1.0
* @date 2020/8/25 11:21 PM
*/
public class GProducerConsumer {
public static void main(String[] args) {
Queue<Bao> queue = new LinkedList<>();
Pro pro = new Pro(queue);
Con con = new Con(queue);
new Thread(pro).start();
new Thread(con).start();
}
}
class Pro implements Runnable {
private final Queue<Bao> queue;
public Pro(Queue<Bao> queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
synchronized (queue) {
// 进来先吼一嗓子
queue.notify();
if (queue.size() > 10) {
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
queue.offer(new Bao());
System.out.println(Thread.currentThread().getName() + ": 生产了一个包");
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Con implements Runnable {
private final Queue<Bao> queue;
public Con(Queue<Bao> queue) {
this.queue = queue;
}
@Override
public void run() {
while (true) {
synchronized (queue) {
// 进来先吼一嗓子
queue.notify();
if (queue.size() <=0 ) {
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
queue.poll();
System.out.println(Thread.currentThread().getName() + ": 消费了一个包");
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Callable
package cn.timevaeless.P多线程;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author timevaeless
* @version 1.0
* @date 2020/8/26 12:34 AM
*/
public class HCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
Thread.sleep(1000);
return sum;
}
public static void main(String[] args) {
//①之前Thread创建的线程,run方法是没有返回值的,而Callable创建的线程有返回值
HCallable callable = new HCallable();
FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();
try {
//
futureTask.isDone();
Thread.sleep(10);
futureTask.cancel(false);
// 阻塞在这直到子线程执行完成
Integer sum = futureTask.get();
System.out.println(sum);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
System.out.println("Over.");
}
}
线程池
/**
* @author timevaeless
* @version 1.0
* @date 2020/8/26 12:46 AM
*/
public class IThreadPool {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 分派任务
// 不带返回值
executorService.submit(() -> {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
});
// 带返回值
Future<Integer> future = executorService.submit(() -> {
int sum = 0;
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
sum += i;
}
return sum;
});
Integer sum = null;
try {
sum = future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
System.out.println("1-20的和为" + sum);
// 关闭线程池
executorService.shutdown();
}
}
网络编程
七层网络模型
OSI(Open System Interconnect),即开放式系统互联,是ISO(国际标准化组织)组织在1985年研究的网络互连模型。
OSI七层模型和TCP/IP五层模型的划分如下:
当发送数据时,需要对发送的内容按照上述七层模型进行层层加包后发送出去。
当接收数据时,需要对接收的内容按照上述七层模型相反的次序层层拆包并显示出来。
如在网上买一个小米10手机,有的是为了宣传,有的是为了防水,总之会为手机包上很多层东西,当收到件之后又会相反的次序拆包。
TCP协议
传输控制协议(Transmission Control Protocol),是一种面向连接的协议,类似于打电话。
建立连接 => 进行通信 => 断开连接
在传输前采用"三次握手"方式。
在通信的整个过程中全程保持连接,形成数据传输通道。
保证了数据传输的可靠性和有序性。
是一种全双工的字节流通信方式,可以进行大数据量的传输。
传输完毕后需要释放已建立的连接,发送数据的效率比较低。
客户端:我是客户端
服务端:我知道你是客户端了,我是服务端
客户端:我知道你知道我是客户端了
客户端:我要断开连接了
服务端:我知道了
服务端:我收拾好东西了,我走了
客户端:好的你走吧
UDP协议
用户数据报协议(User Datagram Protocol),是一种非面向连接的协议,类似于写信。
在通信的整个过程中不需要保持连接,其实是不需要建立连接。
不保证数据传输的可靠性和有序性。
是一种全双工的数据报通信方式,每个数据报的大小限制在64K内。
发送数据完毕后无需释放资源,开销小,发送数据的效率比较高,速度快。
IP地址
192.168.1.1 - 是绝大多数路由器的登录地址,主要配置用户名和密码以及Mac过滤。
IP地址是互联网中的唯一地址标识,本质上是由32位二进制组成的整数,叫做IPv4,当然也有128位二进制组成的整数,叫做IPv6,目前主流的还是IPv4。
日常生活中采用点分十进制表示法来进行IP地址的描述,将每个字节的二进制转化为一个十进制整数,不同的整数之间采用小数点隔开。如:0x01020304 => 1.2.3.4
查看IP地址的方式:
Windows系统:在dos窗口中使用ipconfig或ipconfig/all命令即可
Unix/linux系统:在终端窗口中使用ifconfig或/sbin/ifconfig命令即可
特殊的地址
本地回环地址(hostAddress):127.0.0.1 主机名(hostName):localhost
端口号
IP地址 - 可以定位到具体某一台设备。
端口号 - 可以定位到该设备中具体某一个进程。
端口号本质上是16位二进制组成的整数,表示范围是:0 ~ 65535,其中0 ~ 1024之间的端口号通常被系统占用,建议编程从1025开始使用。
特殊的端口:
- HTTP:80
- FTP:21
- Oracle:1521
- MySQL:3306
- Tomcat:8080
网络编程需要提供:IP地址 + 端口号,组合在一起叫做网络套接字:Socket。
C/S架构
在C/S模式下客户向服务器发出服务请求,服务器接收请求后提供服务。
例如:在一个酒店中,顾客找服务员点菜,服务员把点菜单通知厨师,厨师按点菜单做好菜后让服务员端给客户,这就是一种C/S工作方式。如果把酒店看作一个系统,服务员就是客户端,厨师就是服务器。这种系统分工和协同工作的方式就是C/S的工作方式。
客户端部分:为每个用户所专有的,负责执行前 台功能。
服务器部分:由多个用户共享的信息与功能,招待后台服务。
基于TCP编程模型
/** * @author timevaeless * @version 1.0 * @date 2020/8/29 5:53 PM */ public class TCPServer { public static void startup() throws IOException { // ①定义一个ServerSocket对象用来监听用户请求 ServerSocket serverSocket = new ServerSocket(80); // ②监听请求,每来一个请求,开一个线程来建立连接,然后等待下一个请求 while (true) { // 建立连接 System.out.println("waiting..."); Socket socket = serverSocket.accept(); System.out.println("client " + socket.getInetAddress() + " comming..."); new Thread(new Task(socket)).start(); } } public static void main(String[] args) { try { TCPServer.startup(); } catch (IOException e) { e.printStackTrace(); } } } class Task implements Runnable { private final Socket socket; public Task(Socket socket) { this.socket = socket; } @Override public void run() { // 读出流里的数据 BufferedReader reader = null; // 回写 PrintStream printStream = null; try { reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String content; while (true) { content = reader.readLine(); System.out.println("recv:" + content); if ("bye".equalsIgnoreCase(content)) { break; } printStream = new PrintStream(socket.getOutputStream()); printStream.println("ok."); } } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } if (printStream != null) { printStream.close(); } } } } /** * @author timevaeless * @version 1.0 * @date 2020/8/29 6:05 PM */ public class TCPClient { public static void connect() { // ①创建socket建立连接 // ②发送和接受消息 Socket socket = null; BufferedWriter writer = null; BufferedReader reader = null; try { socket = new Socket("127.0.0.1", 80); String[] arr = {"你好?请问这件东西有优惠吗?", "人在吗?", "???", "Bye"}; int index = 0; while (index < arr.length) { writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); writer.write(arr[index] + "\n"); System.out.println("send: " + arr[index]); writer.flush(); // 等待响应 reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); System.out.println("recv: " + reader.readLine()); Thread.sleep(1000); index++; } } catch (IOException | InterruptedException e) { e.printStackTrace(); } finally { if (reader != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if (writer != null) { try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } } public static void main(String[] args) { TCPClient.connect(); } }基于UDP协议编程模型
/** * @author timevaeless * @version 1.0 * @date 2020/8/29 8:39 PM */ public class UDPReceiver { // 接收方: //(1)创建DatagramSocket类型的对象并提供端口号; //(2)创建DatagramPacket类型的对象并提供缓冲区; //(3)通过Socket接收数据内容存放到Packet中,调用receive方法; //(4)关闭Socket; // 发送方: //(1)创建DatagramSocket类型的对象; //(2)创建DatagramPacket类型的对象并提供接收方的通信地址; //(3)通过Socket将Packet中的数据内容发送出去,调用send方法; //(4)关闭Socket; public static void receive() { // 邮局, 接收方需要提供地址 try (DatagramSocket socket = new DatagramSocket(8888)) { // 用来接收包裹 byte[] bytes = new byte[20]; DatagramPacket packet = new DatagramPacket(bytes, bytes.length); System.out.println("等待包裹到来..."); // 通过邮局接收 socket.receive(packet); System.out.println("recv: " + new String(bytes, 0, packet.getLength())); // 回礼 bytes = "你的包裹已收到".getBytes(); // 根据包裹知道对方的地址 packet = new DatagramPacket(bytes, bytes.length, packet.getAddress(), packet.getPort()); socket.send(packet); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { UDPReceiver.receive(); } } /** * @author timevaeless * @version 1.0 * @date 2020/8/29 9:00 PM */ public class UDPSender { public static void send() { // 邮局 try (DatagramSocket socket = new DatagramSocket()) { byte[] bytes = "Hello UDP".getBytes(); // 包裹上贴上对方地址 DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 8888); // 通过邮局发送 socket.send(packet); // 等待回复 bytes = new byte[30]; packet = new DatagramPacket(bytes, bytes.length); socket.receive(packet); System.out.println("recv: " + new String(bytes, 0, packet.getLength())); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { UDPSender.send(); } }URL类
/** * @author timevaeless * @version 1.0 * @date 2020/8/30 1:27 AM */ public class URLTest { public static void main(String[] args) throws MalformedURLException { URL url = new URL("https://blog.csdn.net/u011423056/article/details/79450845?utm_medium=distribute.pc_relevant.none-task-blog-title-1&spm=1001.2101.3001.4242"); url.getProtocol(); // https url.getHost(); // blog.csdn.net url.getPort(); // -1 url.getPath(); // /u011423056/article/details/79450845 url.getFile(); // /u011423056/article/details/79450845?utm_medium=distribute.pc_relevant.none-task-blog-title-1&spm=1001.2101.3001.4242 BufferedReader reader = null; HttpsURLConnection urlConnection = null; try { urlConnection = (HttpsURLConnection) url.openConnection(); reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); String content; while ((content = reader.readLine()) != null) { System.out.println(content); } } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } if (urlConnection != null) { urlConnection.disconnect(); } } } }
反射
package cn.timevaeless.core.R反射;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.Arrays;
/**
* @author timevaeless
* @version 1.0
* @date 2020/8/30 2:11 PM
*/
public class AReflect {
public void foo() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, NoSuchFieldException {
// ①在面向对象的世界里,万事万物皆对象。
// - 那么Java语言中,类又是谁的对象呢?
// - 答案是类是对象,类是 java.lang.Class类的实例对象。
// - 这个对象如何表示呢?
// foo是Foo类的实例对象
// 类也是对象,是Class类的实例对象,这个对象我们称之为该类的类类型(class type)
// 如Foo类也是一个实例对象,是Class类的实例对象
Fooo foo = new Fooo("a");
// ②Foo类实例化有三种方式,c1/c2 表示了Foo类的类类型
Class<Fooo> c1 = Fooo.class;
Class<? extends Fooo> c2 = foo.getClass();
Class<?> c3 = Class.forName("cn.timevaeless.core.R反射.Fooo");
// 一个类只可能是Class类的一个实例对象
System.out.println(c1 == c2 && c2 == c3);
// ③获取基本数据类型的Class对象
Class<Integer> type = Integer.TYPE; // int 等价于int.class
// 获取包装类的Class对象
Class<Integer> integerClass = Integer.class; // class java.lang.Integer
// ④通过ClassLoader获取Class对象
String.class.getClassLoader(); // null 因为String是由Bootstrap classloader加载的,该加载器不是由java实现的,不可见
ClassLoader classLoader = foo.getClass().getClassLoader(); // jdk.internal.loader.ClassLoaders$AppClassLoader@3d4eac69
Class<?> aClass = classLoader.loadClass("java.lang.Runnable"); // interface java.lang.Runnable
// ⑤感受下通过Class对象在运行时动态创建对象
// System.out.println("请输入要创建对象的全限定名类型:");
// BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
// String classname = reader.readLine();
//
// Object instance = Class.forName(classname).getDeclaredConstructor().newInstance();
// reader.close();
// ⑥Constructor类
Constructor<Fooo> constructor = Fooo.class.getDeclaredConstructor(String.class);
Fooo fooo = constructor.newInstance("小明"); // Fooo{name='小明'}
for (Constructor<?> c : Fooo.class.getConstructors()) {
System.out.println(c.getModifiers() + " " + c.getName() + "(" + Arrays.toString(c.getParameterTypes()) + ")");
}
constructor.getParameterTypes(); // [class java.lang.String]
constructor.getName(); // cn.timevaeless.core.R反射.Fooo
constructor.getModifiers(); // 1 所有修饰符在Modifier类里
// ⑦Field类
// Field name = Fooo.class.getField("name"); // 只能获取public修饰的成员变量
Field field = Fooo.class.getDeclaredField("name"); // 可以获取任意修饰符的成员变量
// field.get(Object obj) - 通过传入一个实例,可以获取该实例的该成员变量当前的值,不过必须是public修饰,否则不可访问
// 不过可以暂时取消访问检查, 这样也可以访问private的成员变量的值了,这种称为暴力反射
field.setAccessible(true);
System.out.println(field.getModifiers() + " " + field.getType() + " " + field.getName() + " = " + field.get(fooo));
field.set(fooo, "小芳"); // Fooo{name='小芳'}
// 获取所有公共的成员变量
for (Field f : Fooo.class.getFields()) {
System.out.println(f.getModifiers() + " " + f.getType() + " " + f.getName());
}
System.out.println();
// 获取所有成员变量
for (Field f : Fooo.class.getDeclaredFields()) {
System.out.println(f.getModifiers() + " " + f.getType() + " " + f.getName());
}
System.out.println();
// ⑧Method类
// Method method = Fooo.class.getMethod("foo"); // 只能获取public修饰的成员方法
Method method = Fooo.class.getMethod("toString");// 可以获取任意修饰符的成员方法
method.invoke(fooo); //Fooo{name='小芳'}
// 获取所有方法,包括父类的,但不包括私有的方法
for (Method m : Fooo.class.getMethods()) {
System.out.println(m.getModifiers() + " " + m.getReturnType() + " " + m.getName() + "(" + Arrays.toString(m.getParameterTypes()) + ")" + " " + Arrays.toString(m.getExceptionTypes()));
}
System.out.println();
// 只获取本类的方法,包括私有的方法
for (Method m : Fooo.class.getDeclaredMethods()) {
System.out.println(m.getModifiers() + " " + m.getReturnType() + " " + m.getName() + "(" + Arrays.toString(m.getParameterTypes()) + ")" + " " + Arrays.toString(m.getExceptionTypes()));
}
// ⑨其他方法
Package aPackage = Fooo.class.getPackage();
aPackage.getName(); // cn.timevaeless.core.R反射
// 获取直接父类
Class<? super Fooo> superclass = Fooo.class.getSuperclass(); // class java.lang.Object
// 获取接口不包括泛型
for (Class<?> interfaceClass : Fooo.class.getInterfaces()) {
System.out.println(interfaceClass);
}
// 获取运行时还存在的注解
for (Annotation annotation : Fooo.class.getAnnotations()) {
System.out.println(annotation);
}
// 获取接口包括泛型
for (Type genericInterface : Fooo.class.getGenericInterfaces()) {
System.out.println(genericInterface);
}
}
// 一个字节码文件一般只有一个类,通过类加载器加载到方法区后,会自动创建出一个Class对象,用来描述该类。
// 反射机制就是用于在运行期间通过Class类动态创建对象并且动态调用方法的机制。
// 在运行状态中,通过Class类
// 对于任意一个类,都能够知道这个类的所有属性和方法
// 对于任意一个对象,都能够调用它的任意一个方法和属性
public static void main(String[] args) {
try {
new AReflect().foo();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@SuppressWarnings("unchecked")
class Fooo<E> implements Comparable<Fooo<E>>, Runnable {
private String name;
public Fooo() {
}
public Fooo(String name) {
this.name = name;
}
@Deprecated
private void foo() throws Exception {}
@Override
public String toString() {
return "Fooo{" +
"name='" + name + '\'' +
'}';
}
@Override
public int compareTo(Fooo o) {
return 0;
}
@Override
public void run() {
}
}