⭐代码和思维导图详见仓库:Sivan_Xin的代码仓库—Java语法。如果觉得有帮助的话,可以帮作者点一个star⭐哦~
🎉 关于Java语法部分的知识体系主要分三篇文章进行讲解:
🎉【基础语法篇】【面向对象篇】【高级语法篇】
🎉大家可以在日常的学习生活中进行参考,笔记内容并不是绝对权威,所以请保持自己独立思考的能力。
本文共16000字,读完约需30分钟。

多个线程可以对内存进行操作,这时就可能会出现一些问题。一个Java程序至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。
并行与并发:
并行:多个CPU同时执行多个任务。
并发:一个CPU同时执行多个任务。比如:秒杀、多个人做同一件事。
问:何时需要多线程?
创建多线程方式一:使用Thread类。
1. 创建一个继承Thread类的子类。
2. 重写Thread类的run方法 -->将线程执行的操作声明在run中。
3. 创建Thread类的子类的对象。
4. 通过对象调用start方法。
start方法的作用:1. 启动线程 2. 调用run方法。
创建多线程方式二:实现Runnable接口。
1. 实现Runnable接口
2. 重写run()方法
3. 创建实现接口的类的对象
4. 使用Thread,将对象传入构造器中,new新的线程对象
5. 使用线程对象调用start()方法
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
- Thread类中的常用方法:
1. start():启动当前线程;调用当前线程的run()
2. run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
3. currentThread():静态方法,返回执行当前代码的线程
4. getName():获取当前线程的名字
5. setName():设置当前线程的名字
6. yield():释放当前cpu的执行权
7. join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态。
8. stop():已过时。当执行此方法时,强制结束当前线程。
9. sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态。
10. isAlive():判断当前线程是否存活
时间片策略; 抢占式:高优先级抢占低优先级的线程。
线程的三大优先级
MAX_PRIORITY:10
MIN _PRIORITY:1
NORM_PRIORITY:5 -->默认优先级
高优先级的线程要抢占低优先级线程cpu的执行权。
如何获取和设置当前线程的优先级:
getPriority():获取线程的优先级
setPriority(int p):设置线程的优先级
说明: 只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。
线程与线程之间可以进行通信交互。
wait()方法:使当前线程阻塞,并将锁释放。与sleep不同,sleep只是将线程休眠,并没有释放锁。
notify()方法:释放正在wait()的线程。
notifyAll()方法:释放所有正在wait()的线程,按照优先级释放。
两张图片:


Thread.State枚举类中定义了线程的几种状态:
synchronized(同步监视器){
//说明:1. 操作共享数据的代码,即为需要被同步的代码。
// 2. 共享数据:多个线程共同操作的变量。
// 3. 同步监视器(锁):任意一个类的对象,但是多个线程必须共用同一把锁。
// 在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
}
同步的好处与局限性
同步解决了线程的安全问题,但是在操作同步代码时,只能有一个线程参与,其他线程等待,相当于是一个单线程的过程,效率低。
线程的死锁问题
不同的线程分别占据对方需要的资源(同步监视器对象)不放弃,就形成了线程的死锁。出现死锁后,不会出现异常,不会出现提示,只是线程都处于阻塞状态,无法继续。
从JDK5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。ReentrantLock类实现了Lock接口,在实现线程安全的控制中,比较常用的是ReentrantLock。
另外的,Java5提供了Future接口来代表Callable接口里call()方法的返回值,并且为Future接口提供了一个实现类FutureTask,这个实现类既实现了Future接口,还实现了Runnable接口。
ExecutorService类、Executors工具类、ThreadPoolExecutor类。推荐使用ThreadPoolExecutor类来创建线程池,避免使用ExecutorService类造成的OOM。
Java 集合框架主要包括两种类型的容器:
Collection接口,在该接口下还有两个子接口List和Set。Map,存储(key—value)映射。通用语句:ArrayList<E> objectName = new ArrayList<>;
toArray方法。Arrays.asList方法。官方文档中是这么介绍的:返回由指定数组支持的固定大小的列表。此方法与Collection.toArray()结合使用,作为基于数组和基于集合的API之间的桥梁。补充知识:
- ArrayList,HashSet,HashMap等都可以用sout进行输出。因为在类的方法中重写了toString方法。
- 可变形参
语法:数据类型···变量名。
在Arrays.asList()方法中传入的是一个可变形参。表示传入的参数可以是一个或多个数/数组。
iterator可用于迭代实现了Collection接口的集合。
next() 1. 指针下移 2. 返回下移后的元素。hasNext() 往下看还有没有其他元素。补充:for:each循环遍历集合就是内部调用迭代器来实现的。
List接口:存储有序的、可重复的数据——动态数组。
接口的具体实现类:ArrayList、LinkedList、Vector。
常用API
参考官方文档/代码即可。
LinkedList的源码分析
LinkedList list = new LinkedList()—> 内部声明了Node类型的first和last属性。主要存储无序的、不可重复的数据,Set中没有新的方法,都是从Collection继承而来的。
向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()。
equals()判断不能返回true。Set的具体实现类:
HashSet、LinkedHashSet、TreeSet。
具体实现类
HashSet
Set的主要实现类,线程不安全,可以存储null值。基于哈希表实现,支持快速查找,但不支持有序性操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。
LinkedHashSet
HashSet的子类。在添加数据的同时,每个数据还维护了pre和next两个引用。LinkedHashSet的底层是一个数组+双向链表。意义和好处是LinkedHashSet中的元素顺序可以保证,也就是说遍历序和插入序是一致的。
TreeSet
向TreeSet中添加数据,要求是相同类的对象。TreeSet的底层使用红黑树。
- TreeSet的两种排序方式
自然排序(需要实现Comparable接口,重写compareTo()方法) 和 定制排序(构造器传参Comparator)
如果使用定制排序,就向TreeSet的构造函数中传入一个Comparator对象。
- 自然排序中,比较两个对象是否相同的标准为:compareTo()返回0。不再是equals()。
- 定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是equals()。
Map存储双列数据(key—value)(以HashMap为例)
key使用Set存储,无序,不能重复。——>存储自定义的类的对象时,要重写equals和hashCode。
value使用Collection存储可重复,无序的数据。——>存储自定义的类的对象时,要重写equals方法。
Entry用Set容器存储,无序,不可重复。调用put方法时放进去的就是Map.Entry对象。(key和value)
Map常用API
可查阅文档/代码仓库。
Collections类直接继承于Object接口,是集合类的一个工具类。这个类不能被实例化(私有构造器),所有的方法都是静态的(可通过类名调用)。包含有关集合操作的静态方法,可以实现对各种集合的搜索、排序、线程安全等。
ArrayList< E>,< E>就是传入一个泛型的意思。 Map<String,Integer> map = new HashMap<>();
Iterator <Map.Entry<String,Integer>> iterator = map.entrySet().iterator();
while(iterator.hasNext()){
Map.Entry<String, Integer> e = iterator.next();
String key = e.getKey();
Integer value = e.getValue();
System.out.println(key + "----" + value);
}
//定义带泛型的类
public class Student<T> {
int age;
String name;
T describe;
public Student(int age, String name, T describe) {
this.age = age;
this.name = name;
this.describe = describe;
}
}
//1. 继承父类时,子类指定泛型的类型,此时子类不是泛型。例:
public class Generic extends fatherGeneric<String>
//接口类似。如果一个类实现的接口给出了具体的泛型,该类不需要写出泛型,反之该类需要给出泛型。
class class -name <type-param-list> implements interface -name <type-arg-list> {
//2. 继承父类时,子类没有指定泛型的类型,此时子类是泛型。
public class Generic <T>extends Student<T>{
//泛型类的构造器不用加<>
public Generic(int age, String name, T describe) {
super(age, name, describe);
}
//泛型方法:
//泛型方法与所在类是否是泛型无关。
//泛型方法可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。
public static <E> List toArray(E []e){
List<E> list=new ArrayList<>();
for(E e1 :e){
list.add(e1);
}
return list;
}
//静态方法中,不能使用泛型。泛型是在实例化对象的时候(调用构造器)使用的,
// 而静态方法在加载类的时候就优先加载了(静态优先,构造随后)。
public static int Demo(T t){ //错误
}
public static void main(String[] args) {
//实例化对象时,要使用泛型。
Generic<String> generic=new Generic<>(12,"Wang","good");
List<Integer> list=Generic.toArray(new Integer[]{1,2,3});
System.out.println(list);
}
}
List<Object> list1 = null;
List<String> list2 = new ArrayList<>();
//编译不通过
// list1 = list2;
List<String> list1 = null;
ArrayList<String> list2 = null;
//编译通过
list1 = list2;
//通配符的使用举例
ArrayList<Object> list=new ArrayList<>();
ArrayList<String> arrayList=new ArrayList<>();
ArrayList<?> a=new ArrayList<>();
//G<?>是G<A> G<B>的父类,故可以父类引用指向子类对象。
a=list;
//添加(写入):对于Arraylist<?>就不能向其内部添加数据。
//查找(读取):对于Arraylist<?>可以读取数据,返回Object类型。
有限制条件的通配符
<? extends Person>
表示?可以接受小于等于Person的类。(-无穷 ,Person],例如Student等…
<? super Person>
表示?可以接受大于等于Person的类。[Person,+无穷),例如Object等…
添加操作和查找操作
添加操作(以List.add为例)
当执行添加操作时,只可以添加小于等于当前区间的子类。(添加Student、Person;不可以添加Object)
查找操作(以List.get为例)
当执行查找操作时,使用当前区间的最大类来接受值。(用Object类来接收)
作用: Java中使用File类的对象来充当文件夹/文件目录。
注意: 在File类中,只是对文件进行创建、删除、重命名等操作,并未进行读取和修改文件内容(IO流)。
相对路径写在main方法下,会对应着当前工程,进而创建文件在当前整体工程下。并非当前Moudle下。
IO流原理
IO是input/output的缩写,用于处理设备之间的数据传输。在Java程序中,对于数据的输入/输出操作是以“流”的形式进行的。
流的分类
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-47doCRZL-1656395185361)(en-resource://database/1322:1)]](https://1000bd.com/contentImg/2022/06/29/014932108.png)

节点流等同于流的基础,所有其他处理流都是建立在节点流的基础之上的。
FileInputStream/FileOutputStream(字节)FileReader/FileWriter(字符)
注意:写入到文件中时(write),文件不需要实际存在,会自动创建。
BufferedInputStream、BufferedOutputStream/BufferedReader、BufferedWriter。
常用BufferedReader提供的readLine方法来读取一行字符。
解码(破解):字节——>字符数组、字符串
编码:字符数组——>字节
InputStreamReader:将字节的输入流转换为字符的输入流(解码)
OutputStreamWriter:将字符的输出流转换为字节的输出流(编码)
简单来说,序列化就是对象转换成二进制流,保存在内存中。
反序列化就是将二进制流转换为对象输出。
ObjectInputStream(反序列化)和ObjectOutputStream(序列化)
字节流。
System.in:标准的输入流,默认从键盘输入。
System.out:标准的输出流,默认从控制台输出。
System.exit(int status):终止当前正在运行的Java虚拟机,这个status表示退出的状态码,非零表示异常终止。(可以返回给其他进程的调用者一个调用的返回码,以根据返回码采取不同的策略。)
注意:不管status为何值程序都会退出,和return 相比有不同的是:return是回到上一层,而System.exit(status)是回到最上层。
字符流。
PrintStream、PrintWriter提供了一系列重载的print() 和 println()。
字节流。
DataInputStream 和 DataOutputStream
作用:用于读入或写出基本数据类型的变量或字符串。
注意:想要写出文件内容,不可以双击打开,应该使用DataOutputSream来写出文件。
直接继承Object类,实现了DataInput和DataOutput接口。
NIO将以更加高效的方式进行文件的读写操作。
Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套是网络编程NIO。
在开始介绍网络编程之前,先介绍一些基本的网络概念:
IP: 唯一的标识 Internet 上的计算机(通信实体)
IPv4:4个字节组成,如192.168.0.1。
IPv6:16个字节组成。
域名: www.baidu.com等,可以通过域名来访问IP地址。
DNS: 域名解析服务器,可以解析域名。
本地回路地址: 127.0.0.1 域名:localhost
端口号: 标识正在计算机上运行的进程(程序)。被规定一个为16位的整数。(0~65535)。不同的进程应该有不同的端口号。
Socket: 端口号和IP地址组合得出一个网络套接字。
TCP协议: 传输前:采用“三次握手”的方式,点对点通信,形成数据通道,保证可靠。
发送结束:释放已建立的链接,但是效率比较低,“四次挥手”断开链接。
UDP协议: 不需要建立连接,不可靠的链接,发送结束无需释放资源,开销小速度快。
URL: 统一资源定位符,对应着互联网的某一资源地址。
一个URL类就表示一个URL。具体方法见仓库。
拓展:端口分类:
- 公认端口:被预先定义的通信服务占用。比如HTTP占用端口80。(0~1023)
- 注册端口:分配给用户进程或应用程序。比如MySQL占用端口3306。比如tomcat的端口号为8080。(1024~49151)
- 动态/私有端口:(49152~65535)
网络编程目的:
直接或间接通过网络协议与其他计算机实现数据交换,进行通讯。
TCP网络编程练习一:客户端发送信息给服务端,服务端将数据显示在控制台上。
客户端:
- 创建InetAddress对象和Socket对象,指明服务器端(现在是本地)IP地址和端口号。
- 使用Socket对象获取输出流。(输出流:将文字从程序输出到网络。)
- 关闭流。
服务端:
- 创建服务端ServerSocket对象,指明自己端口号。
- 创建Socket对象,ServerSocket对象调用accept()方法接受来自客户端的socket。
- 使用Socket对象获取输入流。(输入流:将网络中的信息读取到程序。)
- 创建输出流。(输出流:进一步将程序中的信息写入到内存中。)
注:这里比较特殊,使用ByteArrayOutputStream流读取文字信息到内存。- 关闭流。
TCP网络编程练习二:客户端向服务端发送一个图片,并且服务端保存图片到本地。
TCP网络编程练习三:从客户端发送文件给服务端,服务端保存到本地。并返回“发送成功”给客户端。
与练习一类似,详情参见代码仓库即可。
UDP网络编程练习:客户端发送信息给服务端,服务端将数据显示在控制台上。
客户端:使用DatagramSocket的方法【send】发送DatagramPacket的packet。
服务端:使用DatagramSocket的方法【receive】接收DatagramPacket的packet。
URL网络编程练习:
- 创建URL对象。
- 打开HttpsURLConnection类的对象的链接。
- 使用HttpsURLConnection类的对象打开连接。
- 使用HttpsURLConnection类的对象获取输入流,将URL资源从网络输入到程序。
- 创建输出流,将URL资源输出到内存中。
newInstance()可以创建运行时类的对象。其内部调用了运行时类的空参构造器。获取一个运行时类的属性操作大体如下:
常见的第三步的其他操作:
属性:getDeclaredField(String)—获取指定属性,String为属性名。
方法:getDeclaredMethod(String,参数的Class实例)—获取指定方法,String为方法名。
常见的第五步的其他操作:
属性:set(obj)—设置对象属性值、get()—获取对象属性值。
方法:invoke(obj,args)—表示给方法传参,args就是传递的参数,obj表示该方法属于哪个运行时类的对象。invoke的返回值为方法的返回值。
关于运行时类,我们可以通过一些方法来获取类的结构,比如:
获取当前运行时类的属性(Field)——权限修饰符 数据类型 变量名
获取当前运行时类的方法(Method)——注解 权限修饰符 返回值类型 方法名 参数类型 异常类型
获取当前运行时类的构造器(Constructor)、父类(Superclass)、带泛型的父类(GenericSuperclass)的泛型(ActualTypeArauments)、接口(Interfaces)、所在的包(Package)、类声明的注解(Annotations)。
此内容仅了解即可,不要求掌握,因为在上文调用运行时类的指定结构中用到了。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jkr2nINN-1656395185362)(en-resource://database/765:1)]](https://1000bd.com/contentImg/2022/06/29/014932673.png)
类加载器的作用:把类装载进内存。
了解类加载器以及读取配置文件的两种造流方式:代码见仓库。
动态代理面向切面编程(AOP)到框架的时候再深入学习。
动态代理的应用:Spring 的 AOP 、加事务、加权限、加日志。
⭐码字不易,求个关注⭐
⭐点个收藏不迷路哦~⭐