Java的基础复习(12-5)
文章目录
类和方法
方法修饰符
1.访问控制修饰符
公共访问控制符public、保护访问控制符protected、缺省默认访问控制符、私有访问控制符private
2.非访问控制修饰符
抽象方法控制符abstract 、静态方法控制符static 、最终方法控制符final 、本地方法控制符native 、同步方法控制符synchronized
访问控制修饰符的访问权限
访问级别 | 访问控制修饰符 | 同类 | 同包 | 子类(不同包) | 不同包(其他类) |
---|---|---|---|---|---|
公共 | public | √ | √ | √ | √ |
受保护 | protected | √ | √ | √ | × |
默认 | 缺省修饰符 | √ | √ | × | × |
私有 | private | √ | × | × | × |
引用类型(4种)
为了解决内存泄漏的问题,通过四种引用类型强行调用垃圾回收方法 System.gc()
来解决内存泄漏问题
-
强引用 显式执行
object=null
语句 -
软引用 内存不足时回收对象占用的空间 referent - 新的软引用将引用的对象 q - 该引用向其注册的队列,如果不需要注册,则为 null
-
弱引用 对象拥有更短的生命周期 直接回收被弱引用了的对象
WeakReference
-
虚引用 结合引用关联队列,实现对对象引用关系的跟踪
PhantomReference
使用 instanceof
运算符
二元运算符,测试左边的对象是否是右边类的实例,返回值 boolean
public class test {
public static void main(String[] args) {
System.out.println("string" instanceof Object); //true
}
}
枚举类型
1.5以后 enum
public class test {
public enum Color{
red,blue,yellow,pink,black,white,purple,
}
public static void main(String[] args) {
judje(Color.red);
}
private static void judje(Color color) {
switch (color) {
case red:
System.out.println("red");
break;
case blue:
System.out.println("blue");
default:
System.out.println("other");
break;
}
}
}
泛型
作用: ①数据的安全,约定数据的类型;② 防止类型出错
HashMap<String, Integer> hashMap = new HashMap<>();
比较器
集合、列表的sort()只能自然排序
内部比较
- 实现Comparable接口,重写compareTo方法
- 1: 当前大 0:一样大 -1: 传入的大
public class Student implements MyInterface, Comparable<Object> {
.....
//根据电话降序
@Override
public int compareTo(Object o) {
Student student = (Student) o;
return this.phone < student.phone ? 1 : (this.phone == student.phone ? 0 : -1);
}
- 多级排序
@Override
public int compareTo(Object o) {
// 根据phone id降序
Student student = (Student) o;
int result = this.phone < student.phone ? 1 : (this.phone == student.phone ? 0 : -1);
if (result == 0) {
// string重写了比较方法,默认升序
result = -this.id.compareTo(student.id);
}
return result;
}
外部比较
无侵入性,不需要修改原有的代码
- 定义一个外部比较器,实现Comparator接口
public class MyConparatorWithPhone implements Comparator<Object> {
@Override
public int compare(Object o1, Object o2) {
Student s1 = (Student) o1;
Student s2 = (Student) o2;
return s1.getPhone() - s2.getPhone();
}
}
- 使用
Collections.sort(list, new MyConparatorWithPhone());
自动装箱与自动拆箱
**自动装箱: ** 基本类型—》包装类型 valueOf()
**自动拆箱: ** intValue() 有一个缓冲区 [-128,127] 不在缓冲区范围内就new
数组
package com.gen.test;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] array1 = { 10, 50, 30, 40 };
int[] array2 = { 10, 50, 30, 40 };
int[] array3 = { 10, 50, 30 };
System.out.println(Arrays.equals(array1, array2));
System.out.println(Arrays.equals(array1, array3));
// 默认的是按照升序排序
Arrays.sort(array1);
System.out.println(Arrays.toString(array1));
// 将数组的所有元素赋值为相同的值
Arrays.fill(array3, 100);
System.out.println(Arrays.toString(array3));
// 将数组的长度复制成为一个长度为设定值的新数组
int[] copyOf = Arrays.copyOf(array1, 2);
System.out.println(Arrays.toString(copyOf));
int[] copyOf2 = Arrays.copyOf(array3, 6);
System.out.println(Arrays.toString(copyOf2));
Arrays.sort(array2);
System.out.println(Arrays.binarySearch(array2, 30));
}
}
true
false
[10, 30, 40, 50]
[100, 100, 100]
[10, 30]
[100, 100, 100, 0, 0, 0]
1
常用集合
Collection 接口的接口 对象的集合(单列集合)
├——-List 接口:元素按进入先后有序保存,可重复
│—————-├ LinkedList 接口实现类, 链表, 插入删除, 没有同步, 线程不安全
│—————-├ ArrayList 接口实现类, 数组, 随机访问, 没有同步, 线程不安全
│—————-└ Vector 接口实现类 数组, 同步, 线程安全 实现自动增长的对象数组
│ ———————-└ Stack 是Vector类的实现类
└——-Set 接口: 仅接收一次,不可重复,并做内部排序
├—————-└HashSet 使用hash表(数组)存储元素
│————————└ LinkedHashSet 链表维护元素的插入次序
└ —————-TreeSet 底层实现为二叉树,元素排好序
Map 接口 键值对的集合 (双列集合)
├———Hashtable 接口实现类, 同步, 线程安全
├———HashMap 接口实现类 ,没有同步, 线程不安全
│—————–├ LinkedHashMap 双向链表和哈希表实现
│—————–└ WeakHashMap
├ ——–TreeMap 红黑树对所有的key进行排序
└———IdentifyHashMap
通过迭代器 Iterator
遍历Map集合
package com.gen.test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import com.gen.entity.Student;
public class TestMap {
public static <V> void main(String[] args) {
Student student1 = new Student("12", "zhangsan");
Student student2 = new Student("13", "lisi");
Student student3 = new Student("14","wangwu");
Student student4 = new Student("15", "zhaoliu");
Map<String,Object> map = new HashMap<String, Object>();
map.put(student1.getId(), student1);
map.put(student2.getId(), student2);
map.put(student3.getId(), student3);
map.put(student4.getId(), student4);
Iterator<String> iterator = map.keySet().iterator();
// 取出所有key的集合
while(iterator.hasNext()) {
String key = iterator.next();
System.out.println(map.get(key));
}
for (Object key : map.keySet()) {
System.out.println(map.get(key));
}
}
}
Student [id=12, name=zhangsan]
Student [id=13, name=lisi]
Student [id=14, name=wangwu]
Student [id=15, name=zhaoliu]
Student [id=12, name=zhangsan]
Student [id=13, name=lisi]
Student [id=14, name=wangwu]
Student [id=15, name=zhaoliu]
异常
throw new Exception()
自定义类型异常
public void setId(String id) throws Exception {
if (id.length() < 4 || id.length() > 10) {
throw new Exception("the length of id must 4-10,the giving length is " + id.length());
} else {
this.id = id;
}
}
package com.gen.test;
import com.gen.entity.Student;
public class TestException {
public static void main(String[] args) {
Student student = new Student();
try {
student.setId("123");
student.setName("zhangsan");
} catch (Exception e) {
System.err.println(e.getMessage());
} finally {
System.out.println("end.....");
}
}
}
the length of id must 4-10,the giving length is 3
end.....
异常的分类
类 | 描述 |
---|---|
Throwable | 所有异常的父类 |
Error | 虚拟机抛出的异常 |
Exception | 应用程序抛出和处理的非严重错误 |
RuntimeException | 输出异常的堆栈信息并终止程序运行 |
Checked | 没有继承或者抛出异常,编译错误 |
自定义异常
1.继承 Exception
或者 RuntimeException
2.编写异常类的构造方法,并继承父类实现
package com.gen.exception;
public class NameException extends Exception {
public NameException() {
}
public NameException(String msg) {
super(msg);
}
}
public void setName(String name) throws NameException {
if (name.length() < 3 || name.length() > 5) {
throw new NameException("the length of name must be 3-5 the acutal is " + name.length());
} else {
this.name = name;
}
}
package com.gen.test;
import com.gen.entity.Student;
public class TestNameException {
public static void main(String[] args) {
Student student = new Student();
try {
student.setId("1234");
student.setName("aa");
System.out.println(student);
} catch (Exception e) {
System.err.println(e.getMessage());
} finally {
System.out.println("finished.....");
}
}
}
the length of name must be 3-5 the acutal is 2
finished.....
log4j记录日志的使用
1.log4j.properties
log4j.rootLogger = debug,stdout,D,E
### print info on console ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
### debug info to debugfile ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = D://eclipse-workspace/WEB/Test/src/log/debug.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### error info to errorfile ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File = D://eclipse-workspace/WEB/Test/src/log/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
- TestLog.java
package com.gen.test;
import java.util.InputMismatchException;
import java.util.Scanner;
import org.apache.log4j.Logger;
public class TestLog {
private static Logger logger = Logger.getLogger(TestLog.class.getName());
@SuppressWarnings("resource")
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
System.out.print("input a: ");
int a = scanner.nextInt();
logger.debug("input a " + a);
System.out.print("input b: ");
int b = scanner.nextInt();
logger.debug("input b: " + b);
System.out.printf("%d /%d =%d\n", a, b, a / b);
logger.debug("ouput info " + String.format("%d /%d =%d\n", a, b, a / b));
} catch (InputMismatchException e) {
logger.error("a,b must be integer" + e);
} catch (ArithmeticException e) {
logger.error(e.getMessage());
} finally {
System.out.println("welcome to use....");
}
}
}
2021-03-23 18:24:38 [ main:0 ] - [ DEBUG ] input a 10
2021-03-23 18:24:39 [ main:809 ] - [ DEBUG ] input b: 2
2021-03-23 18:24:39 [ main:818 ] - [ DEBUG ] ouput info 10 /2 =5
2021-03-23 18:28:18 [ main:0 ] - [ DEBUG ] input a 12
2021-03-23 18:28:19 [ main:1387 ] - [ DEBUG ] input b: 0
2021-03-23 18:28:19 [ main:1390 ] - [ ERROR ] / by zero
抽象类和接口
抽象方法: 方法不会有具体的实现实例化,而是抽象类的子类中重写实现;抽象方法被子类使用——代码复用;抽象方法——子类自身的独特性 不能多继承
接口: 提供一种约定(U盘),表示一种能力(软件工程师,设计师) extends 单继承 implements 多接口
文件操作
File对象不仅能表示文件,也能表示目录
操作目录或文件的属性
获得文件的信息
String filePath = "D:\\log\\debug.log";
File file = new File(filePath);
if (file.exists()) {
if (file.isFile()) {
System.out.println(file.isDirectory());
System.out.println("相对路径: " + file.getPath());
System.out.println("绝对路径: " + file.getAbsolutePath());
System.out.println("文件或目录的名称:" + file.getName());
System.out.println("文件长度(单位:字节):" + file.length());
}
}
false
相对路径: D:\log\debug.log
绝对路径: D:\log\debug.log
文件或目录的名称:debug.log
文件长度(单位:字节):361
创建文件
String filePath = "D:\\log\\debug1.log";
File file = new File(filePath);
if (!file.exists()) {
try {
file.createNewFile();
System.out.println("create file successfully!");
} catch (IOException e) {
e.printStackTrace();
}
}
删除文件
String filePath = "D:\\log\\debug1.log";
File file = new File(filePath);
if (file.exists()) {
file.delete();
System.out.println("file deleted!");
}
Java IO流
数据输入输出的抽象
按照处理数据单元划分:
- 字节流:
InputStream / OutputStream
- 字符流:
Reader / Writer
字节流读文本文件
1.字节输入流 InputStream
类 文件的数据输入内存
2.字节输入流 FileInputStream
类
3.FileInputStream
类读取文本文件
String path = "src/com/gen/test/file/a.txt";
File file = new File(path);
FileInputStream in = null;
FileOutputStream out = null;
StringBuffer buffer = new StringBuffer();
try {
in = new FileInputStream(file);
byte[] buf = new byte[in.available()];
// 将文件的内容读到buf中
in.read(buf);
System.out.println(new String(buf));
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
字节流写文本文件
1.字节输出流 OutputStream
类 数据从内存输入到文件
2.字节输出流 FileOutputStream
类
3.FileOutputStream
类写文本文件
String path = "src/com/gen/test/file/a.txt";
FileInputStream in = new FileInputStream(path);
String path1 = "src/com/gen/test/file/b.txt";
FileOutputStream out = new FileOutputStream(path1);
byte [] buf = new byte[10];
int len = -1;
while((len=in.read(buf))!=-1) {
out.write(buf, 0, len);
}
System.out.println("finished!");
out.close();
in.close();
System.exit(0);
字符流读文本文件
字符输入流 Reader FileReader
FileReader fileReader = null;
String pathname = "D:\\log\\a.txt";
try {
fileReader = new FileReader(new File(pathname));
// 创建字符数组作为中转站
char[] cbuf = new char[1024];
StringBuffer buffer = new StringBuffer();
int length;
while ((length = fileReader.read(cbuf)) != -1) {
buffer.append(cbuf);
}
System.out.println(buffer.toString());
}
字符输入流 BufferedReader
类
带有缓冲区,每次把数据读到缓冲区,接着在缓冲区读取数据,避免每次从数据源读取数据进行编码转换
String pathname = "D:\\\\log\\\\a.txt";
BufferedReader bufferedReader = null;
try {
bufferedReader = new BufferedReader(new FileReader(new File(pathname)));
String line = null;
StringBuffer stringBuffer = new StringBuffer();
while ((line = bufferedReader.readLine()) != null) {
stringBuffer.append(line);
}
System.out.println(stringBuffer.toString());
}
字符流写文本文件
字符输出流 Writer FileWriter
String context = "hello world! 世根1111";
String pathname = "D:\\log\\a.txt";
FileWriter fileWriter = null;
try {
fileWriter = new FileWriter(new File(pathname));
fileWriter.write(context);
System.out.println("finished!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fileWriter.close();
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
}
}
字符输出流 BufferedWriter
String context = "hello world! 世根1";
String pathname = "D:\\log\\a.txt";
BufferedWriter bufferedWriter = null;
try {
bufferedWriter = new BufferedWriter(new FileWriter(new File(pathname)));
bufferedWriter.write(context);
System.out.println("finished!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bufferedWriter.close();
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
}
}
读写二进制文件
DataInputStream DataOutputStream
序列化和反序列化
便于将字段和属性保存到磁盘中并从磁盘中还原
序列化概述
将对象的信息转化为字节序列;二进制形态,实现了跨平台特性
序列化保存对象信息
public class Student implements Serializable{
ObjectOutputStream objectOutputStream = null;
String pathname = "D:\\log\\serializable.txt";
try {
objectOutputStream = new ObjectOutputStream(
new ObjectOutputStream(new FileOutputStream(new File(pathname))));
Student student = new Student("111", "zhangsan");
Student student2 = new Student("222", "lisi");
List<Student> list = new ArrayList<Student>();
list.add(student);
list.add(student2);
// 把List序列化
objectOutputStream.writeObject(list);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
反序列化获得对象信息
ObjectInputStream objectInputStream = null;
String pathname = "D:\\log\\serializable.txt";
try {
objectInputStream = new ObjectInputStream(new FileInputStream(new File(pathname)));
List<Student> list = (ArrayList<Student>) objectInputStream.readObject();
for (Student student : list) {
System.out.println(student);
}
System.exit(0);
} catch (IOException | ClassNotFoundException e) {
Java组件
事件处理总结:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zXif1TXj-1617449335789)(C:\Users\ShiGen\Desktop\2020-12-06_201000.png)]
AWT组件库
- Button
- Checkbox
- CheckboxGroup
- Choice
- Canvas 创建自定义组件
- TextField
- TextArea
- List
- Frame
- Dialog
- Filedialog
- Menu
- MenuBar
- Menu 添加到MenuBar中成为下拉菜单
- MenuItem
注解和多线程
注解的分类
注解 | 解释 |
---|---|
内建注解 | |
@Override | 重写父类的某些方法 |
@Deprecated | 标注程序元素已过时 |
@SuppressWarnings | 标识阻止编译器警告,有选择的关闭编译器对类、方法和成员变量等程序元素及其子元素的警告 |
元注解 | |
@Target | 指定被其修饰的注解能用于修饰哪些程序元素 |
@Rentention | 描述了被其修饰的注解是否被编译器丢弃或者保留在class文件中 |
@Documented | 指定被其修饰的注解将被javaDoc工具提取成文档 |
@Inherited | 指定被其修饰的注解将具有继承性 |
多线程
线程: 是进程的一个顺序执行流,为进程执行的多条线索,是调度和执行的基本单位
改善应用程序:
-
充分利用CPU资源——某一个线程发生休眠或阻塞时,CPU恰好处于空闲状态运行其他线程
-
简化编程模型——简化系统(12306购票),易于维护
-
简化异步事件的处理——客户端和服务器建立连接
-
使GUI更有效率
Java多线程的创建以及启动
-
继承
Thread
类 -
实现
Runable
接口(开发中使用最多) -
使用
ExecutorService
Callable
Future
实现
public static void main(String[] args) {
for(int i=0;i<4;i++) {
System.out.println(Thread.currentThread().getName()+"-"+i);
if(i==2) {
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();
myThread2.start();
}
}
}
class MyThread extends Thread{
@Override
public void run() {
for(int i=0;i<4;i++) {
System.out.println(Thread.currentThread().getName()+"--"+i);
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FgjHx6cl-1617449335794)(C:\Users\ShiGen\AppData\Roaming\Typora\typora-user-images\image-20201206205431757.png)]
public static void main(String[] args) {
for(int i=0;i<4;i++) {
System.out.println(Thread.currentThread().getName()+"-"+i);
if(i==2) {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable);
Thread thread2 = new Thread(myRunnable);
thread1.start();
thread2.start();
}
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<4;i++) {
System.out.println(Thread.currentThread().getName()+"--"+i);
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2c8Px43f-1617449335799)(C:\Users\ShiGen\AppData\Roaming\Typora\typora-user-images\image-20201206210306003.png)]
public class Test2 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
System.out.println("program starting......");
Date date1 = new Date();
int taskSize = 5;
// 创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(taskSize);
// 创建有多个返回值的任务
List<Future> list = new ArrayList<Future>();
for(int i=0;i<5;i++) {
MyCallable c = new MyCallable(i+"");
// 执行任务 获取 Future对象
Future future = pool.submit(c);
list.add(future);
}
// 关闭线程池
pool.shutdown();
// 获取所有并发任务地结果
for(Future future : list) {
// 从对象上获取任务的返回值
System.out.println(future.get().toString());
}
Date date2 = new Date();
System.out.println("start to end use "+(date2.getTime()-date1.getTime())+"ms");
}
}
class MyCallable implements Callable<Object>{
private String taskNum;
MyCallable(String taskNum){
this.taskNum = taskNum;
}
@Override
public Object call() throws Exception {
System.out.println(taskNum+" start......");
Date dateTemp1 = new Date();
Thread.sleep(1000);
Date deteTemp2 = new Date();
long time = deteTemp2.getTime()-dateTemp1.getTime();
System.out.println(taskNum+" end......");
return taskNum+" "+time+"ms";
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TwKSSQUs-1617449335818)(C:\Users\ShiGen\AppData\Roaming\Typora\typora-user-images\image-20201206212605060.png)]
Java多线程的优先级和调度
public static final int MAX_PRIORITY 10
public static final int MIN_PRIORITY 1
public static final int NORM_PRIORITY 5
线程优先级的一些特性: Priority
1)线程优先级的无序性:线程优先级有随机的特性
2)线程优先级的继承性:继承关系中优先级一样,线程A启动线程B,那么A线程和B线程的优先级一样
3)线程优先级的规则性:线程会按优先级的大小顺序执行,但并不代表优先级大的一定先执行
调度线程的策略: 抢占式调度策略 时间片轮转调度策略(先调用优先级高的线程)
多线程的线程控制
1.join线程 让一个线程等待另一个线程完成的方法
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
if (i == 5) {
MyThread temp = new MyThread();
try {
temp.start();
temp.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "--" + i);
}
}
}
main---0
......
main---4
Thread-0--0
......
Thread-0--4
main---5
......
main---9
2.后台线程 在后台运行性,为其他线程提供服务(JVM垃圾回收器)–前台的进程死亡,后台的进程也死亡
3.线程休眠 sleep
当前线程暂停一段时间,进入阻塞状态,之后进入可运行状态
// main方法
System.out.println("waiting......");
Wait.waitBySec(5);
System.out.println("finished.....");
——————————————————————————————————————————————————————
class Wait {
public static void waitBySec(int seconds) {
for (int i = 0; i < seconds; i++) {
System.out.println("waiting " + (i + 1) + "s");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
waiting......
waiting 1s
waiting 2s
waiting 3s
waiting 4s
waiting 5s
finished.....
4.线程让步 yield
也可以暂停当前的线程 使线程转入就绪状态;此时,系统选择其它相同或更高优先级线程执行;若无,该线程继续执行
FirstThread firstThread = new FirstThread();
SecondThread secondThread = new SecondThread();
firstThread.start();
secondThread.start();
————————————————————————————————————
class FirstThread extends Thread{
@Override
public void run() {
for(int i=0;i<5;i++) {
System.out.println(Thread.currentThread().getName()+"----"+(i+1));
Thread.yield();
}
}
}
class SecondThread extends Thread{
@Override
public void run() {
for(int i=0;i<5;i++) {
System.out.println(Thread.currentThread().getName()+"----"+(i+1));
Thread.yield();
}
}
}
Thread-1----1
Thread-0----1
......
Thread-1----5
Thread-0----5
sleep
和yield
的区别:①sleep方法给其他线程运行机会时不考虑线程的优先级,因此会给低线程优先级运行的机会,而yield方法只会给相同优先级或者更高优先级线程运行的机会
②线程执行sleep()方法后转入阻塞状态,所以,执行sleep()方法的线程在指定的时间内不会被执行;而yield()方法只是使当前线程重新回到可执行状态,所以执行yield()方法的线程可能在进入可执行状态后马上又被执行
③sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常
④sleep()方法比yield()方法(跟操作系统相关)有更好的可移植性
线程同步
运行的线程需要共享数据
同步代码块
synchronized(object){}
语句块会自动加上内置锁,实现线程的同步
高开销的动作,应尽量减少同步的内容
同步方法
public synchronized void run(){}
同步锁只能是this 或者当前类的字节码对象
代码案例
package com.gen.test.Thread;
public class TestSynchronized {
public static void main(String[] args) {
TestAccount t = new TestAccount();
Thread one = new Thread(t);
Thread two = new Thread(t);
one.setName("张三");
two.setName("张三妻子");
// 启动线程
one.start();
two.start();
}
}
class Account {
private int balance = 500;
public int getBalance() {
return balance;
}
public void withdraw(int amount) {
this.balance = this.balance - amount;
}
}
class TestAccount implements Runnable {
private Account account = new Account();
@Override
public void run() {
for (int i = 0; i < 5; i++) {
makeWithdrawal(100);
if (account.getBalance() < 0) {
System.out.println("账户透支了!!!");
}
}
}
private void makeWithdrawal(int amt) {
if (account.getBalance() >= amt) {
System.out.println(Thread.currentThread().getName() + "准备取款 " + amt);
// 0.5后实现取款
try {
Thread.sleep(500);
account.withdraw(amt);
System.out.println(
Thread.currentThread().getName() + "完成取款,取出 " + amt + " 元,余额为: " + account.getBalance());
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
// 余额不足的提示
System.out.println("操作失败: " + Thread.currentThread().getName() + " 余额为: " + account.getBalance());
}
}
}
--------------------------------------------
张三准备取款 100
张三妻子准备取款 100
张三完成取款,取出 100 元,余额为: 400
张三准备取款 100
张三妻子完成取款,取出 100 元,余额为: 300
张三妻子准备取款 100
张三完成取款,取出 100 元,余额为: 200
张三准备取款 100
张三妻子完成取款,取出 100 元,余额为: 100
张三妻子准备取款 100
张三完成取款,取出 100 元,余额为: -100
张三妻子完成取款,取出 100 元,余额为: -100
账户透支了!!!
操作失败: 张三妻子 余额为: -100
账户透支了!!!
操作失败: 张三妻子 余额为: -100
账户透支了!!!
账户透支了!!!
操作失败: 张三 余额为: -100
账户透支了!!!
操作失败: 张三 余额为: -100
账户透支了!!!
- 同步方法
package com.gen.test.Thread;
public class TestSynchronized {
public static void main(String[] args) {
TestAccount t = new TestAccount();
Thread one = new Thread(t);
Thread two = new Thread(t);
one.setName("张三");
two.setName("张三妻子");
// 启动线程
System.out.println("--------------------------------------------");
one.start();
two.start();
}
}
class Account {
private int balance = 500;
public int getBalance() {
return balance;
}
public void withdraw(int amount) {
this.balance = this.balance - amount;
}
}
class TestAccount implements Runnable {
private Account account = new Account();
@Override
public void run() {
for (int i = 0; i < 5; i++) {
makeWithdrawal(100);
if (account.getBalance() < 0) {
System.out.println("账户透支了!!!");
}
}
}
private synchronized void makeWithdrawal(int amt) {
if (account.getBalance() >= amt) {
System.out.println(Thread.currentThread().getName() + "准备取款 " + amt);
// 0.5后实现取款
try {
Thread.sleep(500);
account.withdraw(amt);
System.out.println(
Thread.currentThread().getName() + "完成取款,取出 " + amt + " 元,余额为: " + account.getBalance());
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
// 余额不足的提示
System.out.println("操作失败: " + Thread.currentThread().getName() + " 余额为: " + account.getBalance());
}
}
}
--------------------------------------------
张三准备取款 100
张三完成取款,取出 100 元,余额为: 400
张三妻子准备取款 100
张三妻子完成取款,取出 100 元,余额为: 300
张三准备取款 100
张三完成取款,取出 100 元,余额为: 200
张三妻子准备取款 100
张三妻子完成取款,取出 100 元,余额为: 100
张三准备取款 100
张三完成取款,取出 100 元,余额为: 0
操作失败: 张三妻子 余额为: 0
操作失败: 张三妻子 余额为: 0
操作失败: 张三妻子 余额为: 0
操作失败: 张三 余额为: 0
操作失败: 张三 余额为: 0
- 同步代码块
package com.gen.test.Thread;
public class TestSynchronized {
public static void main(String[] args) {
TestAccount t = new TestAccount();
Thread one = new Thread(t);
Thread two = new Thread(t);
one.setName("张三");
two.setName("张三妻子");
// 启动线程
System.out.println("--------------------------------------------");
one.start();
two.start();
}
}
class Account {
private int balance = 500;
public int getBalance() {
return balance;
}
public void withdraw(int amount) {
this.balance = this.balance - amount;
}
}
class TestAccount implements Runnable {
private Account account = new Account();
@Override
public void run() {
for (int i = 0; i < 5; i++) {
makeWithdrawal(100);
if (account.getBalance() < 0) {
System.out.println("账户透支了!!!");
}
}
}
private void makeWithdrawal(int amt) {
synchronized (account) {
if (account.getBalance() >= amt) {
System.out.println(Thread.currentThread().getName() + "准备取款 " + amt);
// 0.5后实现取款
try {
Thread.sleep(500);
account.withdraw(amt);
System.out.println(
Thread.currentThread().getName() + "完成取款,取出 " + amt + " 元,余额为: " + account.getBalance());
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
// 余额不足的提示
System.out.println("操作失败: " + Thread.currentThread().getName() + " 余额为: " + account.getBalance());
}
}
}
}
--------------------------------------------
张三准备取款 100
张三完成取款,取出 100 元,余额为: 400
张三妻子准备取款 100
张三妻子完成取款,取出 100 元,余额为: 300
张三准备取款 100
张三完成取款,取出 100 元,余额为: 200
张三妻子准备取款 100
张三妻子完成取款,取出 100 元,余额为: 100
张三准备取款 100
张三完成取款,取出 100 元,余额为: 0
操作失败: 张三妻子 余额为: 0
操作失败: 张三妻子 余额为: 0
操作失败: 张三妻子 余额为: 0
操作失败: 张三 余额为: 0
操作失败: 张三 余额为: 0
问题: 多线程在使用同步机制时,存在“死锁”(多个线程处于等待状态—占用资源,无法运行 无法唤醒)潜在危险
避免方法: ①线程因某个条件未满足而受阻,释放其资源;②若多个对象需互斥访问,确定线程获得锁的先后顺序,整个程序以相反的顺序释放锁
使用特殊域变量(volatile)实现线程同步
使用该域要重新计算 可能导致死锁的状态
volatile和synchronized的区别
- volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
- volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
- volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
- volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
- volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化
使用重入锁实现线程的同步
ReentrantLock
synchronized
最大的作用是避免死锁
使用局部变量实现线程同步
如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
ThreadLocal 类的常用方法 为每个线程提供一个独立的变量副本解决变量并发访问的冲突问题
ThreadLocal() : 创建一个线程本地变量
get() : 返回此线程局部变量的当前线程副本中的值
initialValue() : 返回此线程局部变量的当前线程的"初始值"
set(T value) : 将此线程局部变量的当前线程副本中的值设置为value
线程间的通信
解决生产者和消费者之间的问题
wait() | 当前线程释放锁并进入等待(阻塞)状态 运行状态——等待队列 |
---|---|
notify() | 唤醒一个正在等待响应对象锁的线程,使其进入就绪队列,以便在当前线程释放锁后继续竞争锁 |
notifyAll() | 唤醒所有正在等待响应对象锁的线程,使其进入就绪队列,以便在当前线程释放锁后继续竞争锁 |
- 使用wait()、notify()和notifyAll()需要先调用对象加锁
- 调用wait()方法后,线程状态由Running变成Waiting,并将当前线程放置到对象的等待队列
- notify()和notifyAll()方法调用后,等待线程依旧不会从wait()返回,需要调用notify()和notifyAll()的线程释放锁之后等待线程才有机会从wait()返回
- notify()方法将等待队列中的一个等待线程从等待队列中移到同步队列中,而notifyAll()方法则是将等待队列中所有的线程全部转移到同步队列,被移到的线程状态由Waiting变为Blocked。
- 从wait()方法返回的前提是获得调用对象的锁
package com.gen.test;
/**
* @ClassName : CommunicateThread
* @Author : ShiGen
* @Date: 2021/3/27 15:04
* @Description : ${description}
*/
public class CommunicateThread implements Runnable {
public static void main(String[] args){
CommunicateThread communicateThread = new CommunicateThread ( );
Thread threada = new Thread ( communicateThread, "线程a" );
Thread threadb = new Thread ( communicateThread, "线程b" );
threada.start ( );
threadb.start ( );
}
@Override
synchronized public void run( ){
for(int i = 0; i < 5; i++){
System.out.println ( Thread.currentThread ( ).getName ( ) + "-----" + i );
if(i==2){
try{
wait ( );
} catch(InterruptedException e){
e.printStackTrace ( );
}
}
if(i==1){
notify ( );
}
if(i==4){
notifyAll ( );
}
}
}
}
"D:\Program Files\Java\jdk1.8.0_241\bin\java.exe" "-javaagent:D:\JetBrains\IntelliJ IDEA
线程a-----0
线程a-----1
线程a-----2
线程b-----0
线程b-----1
线程b-----2
线程a-----3
线程a-----4
线程b-----3
线程b-----4
线程通信解决生产者消费者问题
package com.gen.test;
/**
* @ClassName : CommunicationDemo
* @Author : ShiGen
* @Date: 2021/3/27 16:38
* @Description : ${description}
*/
public class CommunicationDemo {
public static void main(String[] args){
sharedResores sharedResores = new sharedResores ( );
new Consuer ( sharedResores ).start ( );
new Producer ( sharedResores ).start ( );
}
}
class sharedResores {
private char c;
// 标记还没有生产
private boolean isProduced = false;
synchronized void putShareChar(char c){
/* 没生产是因为消费者还没有消费——等待
生产了唤醒消费者消费
*/
// 没有在生产是因为在等待消费
if(isProduced){
try{
System.out.println ( "没有消费,生产者停止生产" );
wait ( );
} catch(InterruptedException e){
e.printStackTrace ( );
}
}
this.c = c;
// 已经生产了,通知消费者
isProduced = true;
notify ( );
System.out.println ( "生产了 " + c + " 消费者来消费" );
}
// 同步的方法获得当前的资源
synchronized char getShareChar( ){
if(!isProduced){
try{
System.out.println ( "生产者正在生产,等待一伙儿" );
wait ( );
} catch(InterruptedException e){
e.printStackTrace ( );
}
}
isProduced = false;
notify ( );
System.out.println ( "消费了 " + c + " 通知生产者生产" );
return this.c;
}
}
class Producer extends Thread {
private sharedResores sharedResores;
Producer(sharedResores sharedResores){
this.sharedResores = sharedResores;
}
@Override
public void run( ){
for(char ch = 'A'; ch <= 'D'; ch++){
try{
Thread.sleep ( (int) (Math.random ( ) * 3000) );
} catch(InterruptedException e){
e.printStackTrace ( );
}
sharedResores.putShareChar ( ch );
}
}
}
class Consuer extends Thread {
private sharedResores sharedResores;
Consuer(sharedResores sharedResores){
this.sharedResores = sharedResores;
}
@Override
public void run( ){
char ch = ' ';
while(ch!='D'){
try{
Thread.sleep ( (int) (Math.random ( ) * 3000) );
} catch(InterruptedException e){
e.printStackTrace ( );
}
ch = sharedResores.getShareChar ( );
}
}
}
生产了 A 消费者来消费
消费了 A 通知生产者生产
生产了 B 消费者来消费
消费了 B 通知生产者生产
生产了 C 消费者来消费
消费了 C 通知生产者生产
生产了 D 消费者来消费
消费了 D 通知生产者生产
线程通信的其它方法
1.中断线程 interrupt() 冻结状态——>运行状态
2.守护线程 setDaemon() main线程结束,后台线程也结束
3.join() A线程执行到B线程的join()方法,A就放弃运行资格,处于冻结等待状态,等B线程执行完,A才恢复运行资格;B线程运行中挂掉,A线程用interrupt()清理A线程的冻结状态;join()用来临时加入线程执行
4.yield() 暂时释放执行资格,稍稍减缓线程的切换频率,让多个线程得到的运行资格均等一些
反射机制
在运行的过程中,动态获取信息、动态调用对象方法的功能
获取类的信息
1.获取Class对象
// 调用对象的getClass()方法
Student student = new Student ( );
System.out.println ( student.getClass ( ) );
// _________________________________
// 调用类的class属性
System.out.println ( Student.class );
// 类的forName()静态方法
try{
Class<?> aClass = Class.forName ( "com.gen.entity.Student" );
System.out.println ( aClass );
} catch(ClassNotFoundException e){
e.printStackTrace ( );
}
class com.gen.entity.Student
class com.gen.entity.Student
class com.gen.entity.Student
2.class类对象获得信息
package com.gen.test;
import com.gen.entity.Student;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class TestRefelect {
public static void main(String[] args) throws InvocationTargetException{
// demo02 ();
// demo03 ();
// demo04 ();
// demo05 ();
// demo06 ();
// demo07 ();
// demo08 ( );
demo09 ();
}
static void demo01( ){
// 调用对象的getClass()方法
Student student = new Student ( );
System.out.println ( student.getClass ( ) );
// _________________________________
// 调用类的class属性
System.out.println ( Student.class );
// 类的forName()静态方法
try{
Class<?> aClass = Class.forName ( "com.gen.entity.Student" );
System.out.println ( aClass );
} catch(ClassNotFoundException e){
e.printStackTrace ( );
}
}
// 获得方法
public static void demo02( ){
try{
Class<?> aClass = Class.forName ( "com.gen.entity.Student" );
// 获取所有的公共方法 本类、父类、接口类的所有方法
Method[] methods = aClass.getMethods ( );
for(Method method : methods){
System.out.println ( method );
}
// 只要当前类的
System.out.println ( "==================" );
Method[] declaredMethods = aClass.getDeclaredMethods ( );
for(Method declar : declaredMethods){
System.out.println ( declar );
}
} catch(ClassNotFoundException e){
e.printStackTrace ( );
}
}
// 获取所有的接口方法
static void demo03( ){
try{
Class<?> aClass = Class.forName ( "com.gen.entity.Student" );
Class<?>[] interfaces = aClass.getInterfaces ( );
for(Class<?> inter : interfaces){
System.out.println ( inter );
}
} catch(ClassNotFoundException e){
e.printStackTrace ( );
}
}
// 获取所有的父类,object
static void demo04( ){
try{
Class<?> aClass = Class.forName ( "com.gen.entity.Student" );
Class<?> superclass = aClass.getSuperclass ( );
System.out.println ( superclass );
} catch(ClassNotFoundException e){
e.printStackTrace ( );
}
}
// 获得所有的构造方法
static void demo05( ){
try{
Class<?> aClass = Class.forName ( "com.gen.entity.Student" );
Constructor<?>[] constructors = aClass.getConstructors ( );
for(Constructor<?> constructor : constructors){
System.out.println ( constructor );
}
} catch(ClassNotFoundException e){
e.printStackTrace ( );
}
}
// 获取所有的属性
static void demo06( ){
try{
Class<?> aClass = Class.forName ( "com.gen.entity.Student" );
Field[] fields = aClass.getFields ( );
for(Field field : fields){
System.out.println ( field );
}
} catch(ClassNotFoundException e){
e.printStackTrace ( );
}
}
// 反射代表的实例
static void demo07( ){
try{
Class<?> aClass = Class.forName ( "com.gen.entity.Student" );
Object o = null;
try{
o = aClass.newInstance ( );
Student student = (Student) o;
student.IMethod ( );
} catch(InstantiationException e){
e.printStackTrace ( );
} catch(IllegalAccessException e){
e.printStackTrace ( );
}
} catch(ClassNotFoundException e){
e.printStackTrace ( );
}
}
// 操作属性
static void demo08( ) throws InvocationTargetException{
try{
Class<?> aClass = Class.forName ( "com.gen.entity.Student" );
try{
Student student = (Student) aClass.newInstance ( );
try{
Field nameField = aClass.getDeclaredField ( "name" );
// 开启访问权限
nameField.setAccessible ( true );
nameField.set ( student, "zhangsan" );
System.out.println ( student );
} catch(NoSuchFieldException e){
e.printStackTrace ( );
}
System.out.println ("================" );
// 調取私有方法
try{
Method myMethod = aClass.getDeclaredMethod ( "myMethod",null );
myMethod.setAccessible ( true );
myMethod.invoke ( student,null );
} catch(NoSuchMethodException e){
e.printStackTrace ( );
}
} catch(InstantiationException e){
e.printStackTrace ( );
} catch(IllegalAccessException e){
e.printStackTrace ( );
}
} catch(ClassNotFoundException e){
e.printStackTrace ( );
}
}
// 调用私有带参数方法
static void demo09(){
try{
Class<?> aClass = Class.forName ( "com.gen.entity.Student" );
try{
Method sayName = aClass.getDeclaredMethod ( "sayName", String.class );
sayName.setAccessible ( true );
try{
Student student = (Student)aClass.newInstance ( );
sayName.invoke ( student, "zhangsan" );
} catch(IllegalAccessException e){
e.printStackTrace ( );
} catch(InvocationTargetException e){
e.printStackTrace ( );
} catch(InstantiationException e){
e.printStackTrace ( );
}
} catch(NoSuchMethodException e){
e.printStackTrace ( );
}
} catch(ClassNotFoundException e){
e.printStackTrace ( );
}
}
}
3.动态加载类名和方法名
e.printStackTrace ( );
}
System.out.println ("================" );
// 調取私有方法
try{
Method myMethod = aClass.getDeclaredMethod ( “myMethod”,null );
myMethod.setAccessible ( true );
myMethod.invoke ( student,null );
} catch(NoSuchMethodException e){
e.printStackTrace ( );
}
} catch(InstantiationException e){
e.printStackTrace ( );
} catch(IllegalAccessException e){
e.printStackTrace ( );
}
} catch(ClassNotFoundException e){
e.printStackTrace ( );
}
}
// 调用私有带参数方法
static void demo09(){
try{
Class<?> aClass = Class.forName ( “com.gen.entity.Student” );
try{
Method sayName = aClass.getDeclaredMethod ( “sayName”, String.class );
sayName.setAccessible ( true );
try{
Student student = (Student)aClass.newInstance ( );
sayName.invoke ( student, “zhangsan” );
} catch(IllegalAccessException e){
e.printStackTrace ( );
} catch(InvocationTargetException e){
e.printStackTrace ( );
} catch(InstantiationException e){
e.printStackTrace ( );
}
} catch(NoSuchMethodException e){
e.printStackTrace ( );
}
} catch(ClassNotFoundException e){
e.printStackTrace ( );
}
}
}
#### 3.动态加载类名和方法名
https://blog.csdn.net/weixin_55768452/article/details/115270228