Java核心知识点精讲
一、Java基础
1.集合
集合主要包括List、Set、Map
1.1 List
有序,有3个实现类,分别是ArrayList、Vector、LinkedList。
ArrayList,基于数组实现,线程不安全,查找快,增加、删除操作慢
Vector,基于数组实现,线程安全,查找快,增加、删除操作慢,并发效率低
LinkedList,基于双向链表实现,线程不安全,增加、删除操作快,查找慢。
1.2 Set
不可重复、无序。
对象包括地址内内容。对象的相等性本质上是对象的hashCode值相同,Java依据对象的内存地址计算出对象的HashCode值,如果要比较两个对象是否相等,则必须同时覆盖对象的HashCode方法和equals方法,相等表明两个方法返回的值都相等。
1.3 Map
HashMap,无序,根据键的HashCode值存取。快速更新,jdk1.8基于数组+链表+红黑树实现,线程不安全
ConcurrentHashMap,分段锁实现,线程安全,由多个Segment组成,Segment个数相当于并发数,某一个Segment继承自ReentrantLock并单独加锁。每个Segment安全则全局安全。ConcurrentHashMap的每个Segment内部的数据结构都和HashMap相同。
HashTable,线程安全,继承Dictionary类,同一个时刻只有一个线程写HashTable,并发性不如ConcurrentHashMap。
2. 异常
2.1 异常分类
Throwable是所有错误或异常的父类,Throwable又分为Error、Exception。
Error,Java程序运行错误,启动时出现错误,说明启动失败。运行时出现错误,则系统退出进程。
Exception,Java程序运行时异常。是异常处理的核心。可分为运行时异常和检查异常。
-
RuntimeException:指在Java虚拟机正常运行期间抛出的异常,RuntimeException可以被捕获并处理,如果出现RuntimeException,那么一定是程序发生错误导致的。我们通常需要抛出该异常或者捕获并处理该异常。常见的RuntimeException有NullPointerException、ClassCastException、ArrayIndexOutOfBundsException等。
-
CheckedException:指在编译阶段Java编译器会检查CheckedException异常并强制程序捕获和处理此类异常,即要求程序在可能出现异常的地方通过try catch语句块捕获并处理异常。常见的CheckedException有由于I/O错误导致的IOException、SQLException、ClassNotFoundException等。该类异常一般由于打开错误的文件、SQL语法错误、类不存在等引起。
2.2 异常处理
异常处理有两种,一是抛出异常,另外一种是使用try catch语句处理。
抛出异常,将异常抛给调用者,由调用者根据情况处理。3种方式抛出:throws、throw、系统自动抛出
- throws用在方法上,用于定义方法可能抛出的异常。
- throw作用在方法内,表示明确抛出一个异常。
throws和throw的不同点:
- 位置不同,throws作用在方法上,后面跟着异常的类。throw作用在方法内,后面跟着异常的对象。
- 功能不同,throw后续的代码不再执行,讲跳转到调用者。(finally语句块除外)。
try catch捕获并抛出异常。利用try把可能产生的异常代码包起来.
3. 反射
3.1 概念
在程序运行过程中,能动态的获取该类和对象的信息,即对任意一个类都能获取所有属性和方法,对任意一个对象都能调用其任意一个方法。
3.2 反射应用
对象类型分为两种:编译时类型和运行时类型。
编译时类型,在声明对象时采用的类型。
运行时类型,为对象赋值时采用的类型。
// person对象的编译时类型为Person,运行时类型为Student。
// 真实类型信息(对象的属性和方法)通常通过反射来实现,这便是Java中的反射机制核心功能。
Person person = new Student();
3.3 反射API
Java 的反射API主要用于在运行过程中动态生成类、接口或对象等信息,常用API如下:
- Class类:用于获取类的属性、方法等信息。
- Field类:表示类的成员变量,用于获取和设置类的属性值。
- Method类,表示类的方法,用于获取方法的描述信息和执行某个方法。
- Constructor类,表示类的构造方法。
3.4 反射的步骤
获取类的Class对象 --> 调用所对应类中定义的方法 --> 使用反射API获取并调用类的属性和方法等信息。
获取Class对象的3中方法。
// 1.调用某个对象的getClass方法 Person p = new Person(); Class clazz = p.getClass(); // 2.调用某个类的class属性 Class clazz = Person.class; // 3.调用Class类中的forName静态方法,这是最安全,性能最好的方法。 Class clazz = Class.forName("hello.java.reflect.Person");
3.5 invoke方法
通过Method的invoke方法可以实现动态调用,如动态传入参数和将方法参数化。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G8AHU1gG-1605872302881)(D:\Users\11119761\Desktop\专业进阶\我的笔记\服务端技能点\笔记相关图片\invoke.jpg)]
4. 注解
4.1 概念
Java中设置元素的关联信息和元数据的方法,是一个接口,程序可以通过反射获取指定程序中元素的注解对象,然后通过该注解对象获取注解中的元数据信息。
4.2 标准元注解
元注解负责注解其他注解,标准元注解包括@Target、@Retention、@Documented、@Inherited
- @Target:@Target说明了注解所修饰的对象范围。注解可被用于packages、types(类、接口、枚举、注解类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(循环变量、catch参数等)
- @Retention:@Retention定义了该注解被保留的级别,即被描述的注解在什么级别有效,有以下3种类型。◎ SOURCE:在源文件中有效,即在源文件中被保留。◎ CLASS:在Class文件中有效,即在Class文件中被保留。◎ RUNTIME:在运行时有效,即在运行时被保留。
- @Documented:@Documented表明这个注解应该被javadoc工具记录,因此可以被javadoc类的工具文档化。
- @Inherited:@Inherited是一个标记注解,表明某个被标注的类型是被继承的。如果有一个使用了@Inherited修饰的Annotation被用于一个Class,则这个注解将被用于该Class的子类。
4.3 自定义注解
对注解的使用一般包含定义及使用注解接口,我们一般通过封装统一的注解工具来使用注解。
5. 泛型
泛型的本质是参数化类型,泛型提供了编译时类型的安全检查机制,提高代码安全性和重用性。
5.1 类型擦除
在编码阶段采用泛型时加上的类型参数,会被编译器在编译时去掉,这个过程就被称为类型擦除。因此,泛型主要用于编译阶段。在编译后生成的Java字节代码文件中不包含泛型中的类型信息。例如,编码时定义的List和List在经过编译后统一为List。JVM所读取的只是List,由泛型附加的类型信息对JVM来说是不可见的。
6. 序列化
Java序列化API为处理对象序列化提供了一个标准机制,具体的Java系列化需要注意以下事项。
- 类要实现序列化功能,只需实现java.io.Serializable接口即可。
- 序列化和反序列化必须保持序列化的ID一致,一般使用private static final long serialVersionUID定义序列化ID。
- 序列化并不保存静态变量。
- 在需要序列化父类变量时,父类也需要实现Serializable接口。
- 使用Transient关键字可以阻止该变量被序列化,在被反序列化后,transient变量的值被设为对应类型的初始值,例如,int类型变量的值是0,对象类型变量的值是null。
二、Java并发编程
1. 线程
线程创建
常见的创建Java线程有4种方式:
- 继承Thread类。
- 实现Runnable接口。
- 通过ExecutorService和Callable实现有返回值的线程。
- 基于线程池。
线程状态
线程的生命周期分为新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)这5种状态。
线程基本方法
线程基本方法包括wait、sleep、notify、notifyall、join、yield
线程等待:wait方法,调用wait方法的线程会进入WAITING状态,会释放当前占有的锁,wait方法一般用于同步方法或同步代码块中。
**线程睡眠:sleep方法,**sleep方法不会释放占有的锁,调用后线程进入TIMED-WATING状态。
线程让步:yield方法,调用yield方法会使当前线程让出(释放)CPU执行时间片,与其他线程一起重新竞争CPU时间片。在一般情况下,优先级高的线程更有可能竞争到CPU时间片。
线程加入:join方法,join方法用于等待其他线程终止,如果在当前线程中调用一个线程的join方法,则当前线程转为阻塞状态,等到另一个线程结束,当前线程再由阻塞状态转为就绪状态,等待获取CPU的使用权。
// 在主线程下,调用了子线程的join方法。
System.out.println("子线程运行开始");
ChildThread childThread = new ChildThread();
childThread.join();// 等待子线程childThread执行结束
System.out.println("子线程join()结束,开始运行主线程");
sleep和wait方法区别:
- sleep方法属于Thread类,wait方法属于Object类。
- sleep方法在指定时间暂停执行,让出CPU给其它线程,但会保持监控状态,即线程不会释放对象锁。
- wait方法会释放对象锁,进入等待该对象的等待锁池,需要通过调用此对象的notify方法唤醒。
2. 锁
锁的作用
Java中的锁主要用于保障多并发线程情况下数据的一致性,保障同一个时刻只有一个线程持有该对象的锁并修改对象,保障数据安全
锁分类
锁的分类角度 | 类别 |
---|---|
乐观和悲观 | 乐观锁、悲观锁 |
获取资源的公平性 | 公平锁、非公平锁 |
是否共享资源 | 共享锁、独占锁 |
锁的状态 | 偏向锁、轻量级锁、重量级锁 |
乐观锁
认为每次读取数据时别人都不会修改数据,所以不上锁,更新数据的时候会判断别人有没有更新数据,通常采用先读取当前版本号然后加锁的方法。Java中的乐观锁大部分是通过CAS(Compare And Swap,比较和交换)操作实现的,CAS是一种原子更新操作,在对数据操作之前首先会比较当前值跟传入的值是否一样,如果一样则更新,否则不执行更新操作,直接返回失败状态。
悲观锁
悲观锁采用悲观思想处理数据,在每次读取数据时都认为别人会修改数据,所以每次在读写数据时都会上锁,这样别人想读写这个数据时就会阻塞、等待直到拿到锁。Java中的悲观锁大部分基于AQS(Abstract Queued Synchronized,抽象的队列同步器)架构实现。AQS定义了一套多线程访问共享资源的同步框架,许多同步类的实现都依赖于它,例如常用的Synchronized、ReentrantLock、Semaphore、CountDownLatch等。该框架下的锁会先尝试以CAS乐观锁去获取锁,如果获取不到,则会转为悲观锁(如RetreenLock)。
3. CAS
什么是CAS?
CAS表示(Compare And Swap)值比较并交换,CAS算法CAS(V, E, N)包含3个参数,V表示要更新的变量,E表示预期的值,N表示新值。在且仅在V值等于 E值时,才会将V值设为 N,如果 V值和 E值不同,则说明已经有其他线程做了更新,当前线程什么都不做。最后,CAS返回当前V的真实值。
CAS的特性:乐观锁、非阻塞、自旋等待。
4. AQS
AQS(Abstract Queued Synchronizer)是一个抽象的队列同步器,通过维护一个共享资源状态(Volatile Int State)和一个先进先出(FIFO)的线程等待队列来实现一个多线程访问共享资源的同步框架。
三、网络与负载均衡
1. CDN
概念
CDN(Content Delivery Network,内容分发网络)指基于在各地部署的机房服务器,通过中心平台的负载均衡、内容分发、调度的能力,使用户就近获取所需内容、降低网络延迟、提升用户访问的响应速度和体验度。
内容分发系统
将用户请求的数据分发到就近的各个中心机房,以保障为用户提供快速、高效的内容服务。缓存的内容包括静态图片、视频、文本、用户最近访问的JSON数据等。缓存的技术包括内存环境、分布式缓存、本地文件缓存等。缓存的策略主要考虑缓存更新、缓存淘汰机制。
负载均衡系统
负载均衡是CDN的核心,根据当前网络的流量分布、各地机房服务器的负载和用户请求的特点进行负载到不同的服务器上。负载均衡系统包括全局负载均衡(GSLB)和本地负载均衡(SLB)
-
全局负载均衡主要指跨机房的负载均衡,通过DNS解析或者应用层重定向技术将用户的请求负载到就近的中心机房上。
-
本地负载均衡主要指机房内部的负载均衡,一般通过缓存服务器,基于LVS、Nginx、服务网关等技术实现用户访问的负载
2. 负载均衡
四层负载均衡基于IP和端口的方式实现网络的负载均衡,具体实现为对外提供一个虚拟IP和端口接收所有用户的请求,然后根据负载均衡配置和负载均衡策略将请求发送给真实的服务器。
七层负载均衡基于URL等资源来实现应用层基于内容的负载均衡,具体实现为通过虚拟的URL或主机名接收所有用户的请求,然后将请求发送给真实的服务器。
四层负载均衡和七层负载均衡的最大差别是:四层负载均衡只能针对IP地址和端口上的数据做统一的分发,而七层负载均衡能根据消息的内容做更加详细的有针对性的负载均衡。
我们通常使用LVS等技术实现基于Socket的四层负载均衡,使用Nginx等技术实现基于内容分发的七层负载均衡
四、数据库
1. 数据库基本概念和原则
存储引擎
数据库管理系统(DBMS)使用存储引擎创建、查询和更新数据,不同存储引擎有不同的存储机制、索引技巧、锁定水平。
MyIASM是MySQL默认的存储引擎,不支持数据库事务、行级锁和外键,因此在INSERT(插入)或UPDATE(更新)数据即写操作时需要锁定整个表,效率较低。MyIASM的特点是执行读取操作的速度快,且占用的内存和存储资源较少。它在设计之初就假设数据被组织成固定长度的记录,并且是按顺序存储的。在查找数据时,MyIASM直接查找文件的OFFSET,定位比InnoDB要快(InnoDB寻址时要先映射到块,再映射到行)。总体来说,MyIASM的缺点是更新数据慢且不支持事务处理,优点是查询速度快。
InnoDB为MySQL提供了事务(Transaction)支持、回滚(Rollback)、崩溃修复能力(Crash Recovery Capabilities)、多版本并发控制(Multi-versioned Concurrency Control)、事务安全(Transaction-safe)的操作。InnoDB的底层存储结构为B+树,B+树的每个节点都对应InnoDB的一个Page,Page大小是固定的,一般被设为16KB。其中,非叶子节点只有键值,叶子节点包含完整的数据,如图所示。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-67tAMPjX-1605872302888)(笔记相关图片/InnoDB.jpg)]
InnoDB适用于有以下需求的场景。
◎ 经常有数据更新的表,适合处理多重并发更新请求。
◎ 支持事务。
◎ 支持灾难恢复(通过bin-log日志等)。
◎ 支持外键约束,只有InnoDB支持外键。
◎ 支持自动增加列属性auto_increment。
五、设计模式
设计模式是经过高度抽象化的在编程中可以被反复使用的代码设计经验的总结。
1.工厂模式
工厂模式是最常见的设计模式,它的本质是用工厂反复代替new操作创建一种实例化对象的方式
// 1.定义接口
public interface Phone{
String brand();
}
//2. 定义实现类
public class IPhone implements Phone{
@Override
public String brand(){
return "this is a Apple phone";
}
}
public class HuaWei implements Phone{
@Override
public String brand(){
return "this is a HuaWei phone";
}
}
// 3.定义工厂类
public class Factory{
public Phone createPhone(String phoneName){
if("HuaWei".equals(phoneName)){
return new HuaWei();
}else if("Apple".equals(phoneName)){
return new IPhone();
}else{
return null;
}
}
}
// 4.使用工厂模式
public static void main(String[] args){
Factory factory = new Factory();
Phone Huawei = factory.createPhone("HuaWei");
Phone Huawei = factory.createPhone("Apple");
}
2. 抽象工厂模式
在工厂模式上添加了一个创建不同工厂的抽象接口(抽象类或接口实现),该接口可叫作超级工厂。在使用过程中,我们首先通过抽象接口创建出不同的工厂对象,然后根据不同的工厂对象创建不同的对象。
public abstract class AbstractFactory{
public abstract Phone createPhone(String brand);
public abstract Computer createComputer(String brand);
}
3. 单例模式
单例模式保证了唯一性,即在整个系统中同一时刻只有一个类的实例存在。
单例模式的常见写法有懒汉模式(线程安全)、饿汉模式、静态内部类、双重校验锁
// 1.懒汉模式
public class LazySingleton{
// 定义一个私有的静态对象instance。
private static LazySingleton instance;
private LazySingleton(){}
public static synchronized Lazysingleton getInstance(){
if (instance == null){
instance = new Lazysingleton();
}
return instance;
}
}
// 2.饿汉模式
public class HungrySingleton{
// 懒汉模式. 定义一个私有的静态对象instance。
private static HungrySingleton instance = new HungrySingleton();
private HungrySingleton(){}
public static synchronized Lazysingleton getInstance(){
return instance;
}
}
参考书籍《Offer来了:Java核心知识点精讲》
本文地址:https://blog.csdn.net/u014775977/article/details/109862312