JavaSE
此笔记为作者复习所写,未经允许不得转发,可供读者参考
*基本知识点
对象赋值
Person p1 = p;
含义:把p的地址传给p1,导致p1和p指向堆空间中的同一个对象实体(若Person对象不重写toString方法,那么输出对象时即为地址)
String
判断字符串是否相等
(1) Objects.equals(String s1, String s2);
(2) s1.equals(b);
String方法
String trim() 返回str副本,即忽略前导空白和尾部空白
int compareTo() 返回第一个不同字母的ASCII之差
boolean startsWith(String str) 判断str是否以str开始
boolean endsWith(String str) 判断str是否以str结束
boolean matches(regex) 判断是否匹配(注:regex是正则表达式 Regular Expression)
String 与 char[] 的转换
String -> char[] str.toCharArray()
char[] -> String (1) Arrays.toString(char[]) [a, b, c]
(2) String str = new String(char[]) abc
String 与 bytes[] 的转换
编码:String -> bytes[] str.getBytes[]
解码:bytes[] -> String String str = new String(bytes[])
String特性
(1) final类,不可变的字符序列
(2) 实现了Serializable接口,支持序列化
(3) 实现了Comparable接口,可比较大小
(4) 通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串声明在字符串常量池中,字符串常量池中不会存储相同内容的字符串,当对字符串重新赋值时,需要重新指定内存区域赋值,不能在原有的value上赋值(意思是重新赋值的字符串的地址会改变)
String s1 = "abc";
String s2 = "def";
String s3 = s1 + s2;
String s4 = "abcdef";
String s5 = "abc" + "def";
String s6 = s3.intern();//返回值s6在常量池中
System.out.println(s3 == s4)//false
System.out.println(s4 == s5)//true
System.out.println(s4 == s6)//true
可变形参个数的方法
① 格式:数据类型 . . . 变量名 (eg: String . . . str)
② 当同时存在String str和String . . . str时,构成重载, 且当传入一个String参数时,优先进入String str
Lambda表达式和方法引用
Lambda格式:(形参列表) -> Lambda体
->:Lambda操作符
Lambda体:重写的抽象方法的方法体
当方法体中只有一条语句时,可以省略{}和return
使用条件:必须是函数式接口(@FunctionalInterface),即接口中只声明了一个抽象方法
Lambda的本质:作为接口的实例(即对象)
方法引用格式:类(or对象)::方法名
//原始
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
//Lambda
Comparator<Integer> comparator = (o1, o2) -> o1.compareTo(o2);//因为只有一条语句,故省略了{},return
//方法引用
Comparator<Integer> comparator = Integer::compareTo;
//方法引用用于遍历(ps:比较有逼格)
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.forEach(System.out::println); //拿来炫
*关键字
instanceof
① (boolean) a instanceof A : 判断a 是否是A 的实例
② 较高级的数据类型 -> 较低级的数据类型 (强制类型转化)
较低级的数据类型 -> 较高级的数据类型 (自动类型提升)
③ 父类 -> 子类 (向下转型,同强制类型转化)
子类 -> 父类 (向上转型,多态)
为了避免ClassCastException异常,向下转型前需要用instanceof判断
④ 如果 a instanceof A == true && a instanceof B == true
那么 A 是 B 的子类
static
实例变量(非静态):创建的类的多个对象,每个对象独立拥有一套非静态属性,彼此互不干扰
-静态变量:多个对象共享同一个静态变量,当修改某个对象的静态变量时,其他对象的此静态变量也会被修改
① static修饰属性(静态属性):
(1) 静态属性随着类的加载而加载,可用"类.静态变量"来调用
(2) 静态变量的加载早于对象
(3) 静态变量存在方法区的静态域中
② static修饰方法(静态方法):
(1) 通过"类.静态方法"来调用
(2) 静态方法中,只能调用静态属性或方法;而非静态方法既能调用非静态,也能调用静态
经验之谈:(1) 若属性可被多个对象共享,不会因为对象的不同而改变,那么该属性可设置为static
(2) 操作静态属性的方法,及工具类的方法,声明为static
final
① 修饰类:此类不可被继承(没有子类)(例如:String,System,StringBuffer)
② 修饰方法:此方法不可被重写 (例如:Object类中的getClass())
③ 修饰变量:常量
④ static final 修饰变量:全局常量
多线程
概念
程序(program):一段静态的代码
进程(process):正在运行的一个程序
线程(thread):进程可细化为线程,是程序内部的执行路径
并行:多个CPU同时执行多个任务
并发:一个CPU(采用时间片),“同时”执行多个任务
一个java应用程序,至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程
多线程的创建
法1:继承Thread类
步骤:继承Thread类 -> 重写run() -> 创建子类对象 -> 调用start()
法2:实现Runnable接口
步骤:创建一个类实现Runnable接口 -> 创建对象 -> 将此对象作为参数传递给Thread类的构造器,创建Thread类对象 -> Thread类对象调用start()
法3:实现Callable接口
public class ThreadNew implements Callable {
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
System.out.println(i);
}
return sum;
}
}
@Test
public void test() throws ExecutionException, InterruptedException {
ThreadNew threadNew = new ThreadNew();
FutureTask futureTask = new FutureTask(threadNew);
new Thread(futureTask).start();
System.out.println(futureTask.get());
}
线程的生命周期
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8FLVgngg-1656840376560)(C:\Users\一熹\AppData\Roaming\Typora\typora-user-images\image-20220702190859361.png)]
线程同步(线程安全问题)
法1:synchronized
synchronized(同步监视器){
//需要被同步的代码
//即:操作共享数据的代码
//同步监视器:俗称锁,要求多个线程共用一把锁
}
法2:同步方法
在操作共享数据代码的方法上标注synchronized
法3:Lock
private ReentrantLock lock = new ReentrantLocak();
lock.lock();
//需要被同步的代码
lock.unlock();//与synchronized的区别:需要手动解锁
常用方法
start() 启动当前线程,调用当前线程的run()
run() 重写run(), 将需要进行的操作声明在内
currentThread() 返回当前的线程
getName() 返回线程的名字
setName() 设置线程的名字
yield() 释放当前CPU的执行权
join() 在a中调用b的join,a进入阻塞状态,直到b执行完以后,a才结束阻塞状态
sleep(long millionTime) 让当前线程睡眠指定时间
isAlive() 判断线程是否存活
线程的通信
wait() 执行后,当前线程阻塞,并释放同步监视器
notify() 执行后,唤醒被wait()的一个线程
notifyAll() 执行后,唤醒所有被wait()的线程
使用前提:必须使用在synchronized修饰的同步代码块或同步方法中
线程的调度
① 调度策略:时间片(同优先级)
抢占式(不同优先级:高优先级的线程抢占CPU)
② 优先级:Thread.MIN_PRIORITY = 1;Thread.NORM_PRIORITY = 5;Thread.MAX_PRIORITY = 10
③ 方法:setPriority() getPriority()
bug
① junit 对 多线程进行单元测试,大概率会运行不完所有线程
原因:junit在运行时,在主线程结束后就关闭了进程(System.exit()方法,将JVM关闭,其他线程直接挂哩),不会等待各个线程运行结束
解决方法:Thread.sleep(long millionTime)让main线程阻塞一会儿,等着其他线程结束
*设计模式
单例设计模式
饿汉式
① 私有化构造器
② 内部创建对象
③ 提供公共静态方法返回对象
public class Bank{
private Bank(){}
private static Bank instance = new Bank();
public static Bank getInstance(){
reuturn instance;
}
}
public static void main(String[] args){
Bank bank = Bank.getInstance;
}
##### 懒汉式
public class Bank{
private Bank(){}
private static Bank instance = null;
public static Bank getInstance(){
if(instance == null)
instance = new Bank();
return instance;
}
}
线程安全的懒汉式
public class Bank {
private Bank() {}
private static Bank instance = null;
public static Bank getInstance() {
if (instance == null) {
synchronized (Bank.class) {
if (instance == null) {
instance = new Bank();
}
}
}
return instance;
}
}
优缺点比较:
饿汉式:优点:线程安全。缺点:对象加载时间过长
懒汉式:优点:延迟对象的创建。缺点:线程不安全(需要同步)
模板方法设计模式
在实现一个算法时,整体步骤固定通用,且已经在父类中写好了。但某些部分易变,易变部分可以抽象出来,供不同子类实现
//计算某段代码执行所花费的时间
public void spendTime(){
long start = System.currentTimeMillis();
code();//不确定、易变的部分
long end = System.currentTimeMillis();
System.out.println("花费的时间为:" + (end - start));
}
代理模式
为其他对象提供一种代理以控制对这个对象的访问
工厂模式
实现创建者与调用者的分离,即将创建对象的具体过程隐蔽起来,达到提高灵活性的目的
类
工具类
Objects类
① equals()
equals() 与 == 的区别
==:(1) 用于基本数据类型(boolean除外):比较数据
(2) 用于引用数据类型(类,数组,接口):比较地址值
equals():虽然String是引用数据类型,但是像String、Date、File、包装类等都重写了equals()方法,因此比较数据,而非地址
//重写equals() 简略版
public boolean equals(Object obj){
if(this == obj)
return true;//比较地址
if(obj instanceof User){
User user = (User)obj;
return this.age == user.getAge() && this.name.equals(user.getName());
}
return false;
}
② toString():(1) 当我们输出一个对象时,实际上是调用的这个对象的toString()方法
toString()源码:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
③ 像String、Date、File、包装类等都重写了toString()方法,调用时返回实体内容信息
Arrays类
(1) boolean equals (int[] a, int[] b); 判断a, b是否相等
(2) String toString(int[] a); 输出[, , ,]
(3) void fill (int[] a, int value); 将value替换a中的所有值
(4) void sort(int[] a); 排序
(5) int binarySearch(int[] a, int value); 二分法查找递增数组中的value,并返回索引
Collections类
(1) shuffle(List) 随机
(2) sort(List) / sort(List, Comparator) 排序
(3) int frequency(List, Object) 返回obj出现的次数
(4) copy(dest, src)
(5) synchronizedXXX() 解决线程安全问题
包装类
① 基本数据类型 byte/short/int/long/float/double/boolean/char
包装类Byte/Short/Integer/Long/Float/Double:父类是Number
Boolean/Character
② 装箱: 基本类 -> 包装类 : 调用包装类的构造器: Integer integer = new Integer(1);
拆箱: 包装类 -> 基本 : 调用包装类的xxxValue(); int i = integer.intValue();
③ 自动装箱: Integer integer = 1;
自动拆箱: int i = integer;
④ 基本 -> String:(1) String str1 = num1 + “”;
(2) String -> 基本:String str1 = String.valueOf(num1);
String -> 基本:int num1 = Integer.parseInt(str);
System类
(1)currentTimeMillis 时间戳
(2)void exit(int status) 0正常退出,非0异常退出
(3)void gc() 垃圾回收
(4)String getProperty(String key) key:java.home/java.version/os.name/os.version/user.name/user.home/user.dir
Collection类
-Collection 单列集合,用来存储一个一个对象
-List 存储有序,可重复数据 (动态数组)
-Set 存储无序,不可重复数据 (集合)
-Map 双列集合,存储一对一对(key->value键值对)的数据(函数映射,可多对一,不可一对多)
Colletion
//方法引用
collection.forEach(System.out::println); //遍历Collection元素
Iterator
集合遍历
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
Iterator iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
//hasNext() 判断是否还有下一个元素
//next() 遍历一个元素(指针下移,将下移后集合位置上的元素返回)
//remove() 在遍历时可以移除某个元素
//注意:集合对象每次调用iterator()方法都会得到一个全新的迭代器对象,默认游标在集合的第一个元素之前(-1)
*List
###### ArrayList
//建议使用带参构造器
ArrayList list = new ArrayList(int capacity)
/*
jdk7: 1.底层创建了长度为10的Object[]
2.如果容量不够,扩容成1.5倍
jdk8: 1.Object[]初始化无长度
2.第一次调用add(),底层才创建长度10的数组
3.后续与jdk7相同
小结:jdk7类似于单例模式的饿汉,jdk8类似于懒汉,延迟了数组的创建,节省了内存
*/
LinkerList
内部声明了Node类型的first,last属性,默认为null,Node的定义体现了双向链表
Vector
底层创建长度为10的数组,扩容为2倍
*Set
HashSet
添加元素的过程:
1.计算对象的hash值,通过一定的算法将哈希值转成index
2.在此索引处存入该对象
3.若两个对象索引相同:
3.1先比较哈希值:若不同,则将2个对象以链表的形式存储
3.2 若相同:比较equals(),若返回值为true,则两个对象真的相等
返回值为false,再以链表形式存储
小结:HashSet底层:数组+链表
相同的对象,必须具有相同的hashcode()
LinkedHashSet
在添加数据的同时,每个数据还维护2个引用,记录此数据的前一个和后一个(类似于链表)
优点:频繁遍历操作,效率优于HashSet
TreeSet
向TreeSet中添加的数据,必须是相同类的对象
TreeSet依靠compareTo比较是否相等,而不是equals()
可用自然排序(comparable),定值排序(comparator)
*Map
HashMap
key:无序、不可重复,用Set存储 -> key所在类重写equals(),hashcode()
value:无序、可重复,用Collection存储 -> value所在类重写equals()
一个键值对构成一个Entry对象
Map中的Entry:无序不可重复,用Set存储
//底层原理
/*
jdk7:实例化时底层创建一个16的数组Entry[] table
扩容为2倍(当超出临界值12,且存放的位置非空)
(底层结构:数组+链表)
jdk8:实例化时长度为0
首次调用put()时,才创建数组
当数组的索引的链表长度>8且数组长度>64时,链表改为红黑树,否则扩容
(底层结构:数组+链表+红黑树)
*/
LinkedHashMap
同LinkedHashSet
TreeMap
只能按照key排序
枚举类
public enum Season {
SPRING("春天"),
SUMMER("夏天"),
AUTUMN("秋天"),
WINTER("冬天");
private final String seasonName;
Season(String seasonName) {
this.seasonName = seasonName;
}
@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
'}';
}
}
常用方法:
(1)toString 打印枚举名称
(2)values 获取所有枚举类名称
(3)valueOf(String str) 将字符串转化成对应的枚举类对象
Java比较器
给对象排序,使用Comparable或Comparator接口。像String、包装类等实现了Comparable接口,重写了CompareTo方法
自然排序
对自定义的类来说,如果需要排序,就要实现Comparable接口,重写CompareTo方法
//先按价格排序,再按姓名排序
@Override
public int compareTo(Object o) {
if (o instanceof Goods) {
Goods goods = (Goods) o;
if (this.getPrice() > goods.getPrice()) {
return 1;
} else if (this.getPrice() < goods.getPrice()) {
return -1;
} else {
return this.getName().compareTo(goods.getName());//String类已经重写了compareTo()
}
} else {
return 0;
}
}
定制排序Comparator
Arrays.sort(arr, new Comparator<Goods>() { //此处可替换为lambda表达式
@Override
public int compare(Goods o1, Goods o2) {
if(o1.getPrice() > o2.getPrice()){
return 1;
}else if(o1.getPrice() < o2.getPrice()){
return -1;
}else {
return o1.getName().compareTo(o2.getName());
}
}
});
IO流
File类
File类一个对象,代表一个文件或文件目录(dir)
creatNewFile 创建文件
mkdir/mkdirs 创建文件夹
delete file/dir 删除
原理及分类
输入:外部数据(磁盘) -> 内存(程序)
输出:程序(内存) -> 外部数据
分类:
字节流(8bit) 字符流(16bit)
输入流 输出流
节点流 处理流
注意:文本文件:txt、java、c、cpp 使用字符流
非文本文件:jpg、mp3、mp4、avi、doc、ppt 使用字节流
抽象基类:
字节输入流:InputStream
字节输出流:OutputStream
字符输入流:Reader
字符输出流:Writer
流的体系结构
节点流(文件流)
FileInputStream、FileOutputStream、FileReader、FileWriter
缓冲流
提高流的读取、写入速度
Buffered~
flush刷新缓冲区
转换流
InputStreamReader 字节输入流 -> 字符输入流 (解码)
OutputStreamWriter 字符输出流 -> 字节输出流 (编码)
字符集
ASCII、GBK、Unicode、UTF-8、ANSI
标准输入输出流
System.in 标准输入流
System.out 标准输出流
打印流
PrintStream 输出字节流
PrintWriter 输出字符流
数据流
DataInputStream
DataOutputStream
对象流
ObjectOutputStream
ObjectInputStream
public static final long serialVersionUID 序列化版本号
随机存储文件流
RandomAccessFile
网络编程
基础知识
IP(Internet Protocol):InetAddress类:标识互联网上的计算机。分类:ipv4,ipv6,局域网,万维网
域名:www.baidu.com
DNS:域名解析服务器
本地回路地址:127.0.0.1 对应:localhost(域名)
端口号:标识正在计算机上运行的程序,不同进程有不同的端口号(1~65535)
网络协议
TCP:使用前建立连接,形成通道:三次握手,四次挥手;客户端,服务端;进行大数据量传输;效率低(类似于打电话)
UDP:不需要建立连接,将数据、源、目的封装成数据包;无需确认;可广播发送;结束时无需释放资源,开销小,速度快(类似于发短信)
URL网络编程
url:统一资源定位符,表示Internet上某一资源的地址
构成:<传输协议>://<主机名>:<端口号>/<文件名>#片段名?参数列表
eg:http://192.168.1.100:8080/helloworld/index.jsp#a?username=shkstart&password=123
*反射
反射被视为动态语言的关键,允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
类的加载过程
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class后缀),接着我们使用java.exe命令对某个字节码文件进行解释运行,相当于将某个字节码文件加载到内存中,此过程称为类的加载。加载到内存中的类,称为运行时类,此类就作为Class的一个实例
即Class的一个实例就对应一个运行时类
加载到内存中的运行时类,会缓存一定的时间,可通过以下方式来获取(非new)
获取运行时类
//1.调用类.class
Class clazz1 = Person.class;
//2.通过运行时类的对象
Person p1 = new Person();
Class clazz2 = p1.getClass();
//***3.通过Class的forName
Class clazz3 = Class.forName("Person");//全类名
//4.类的加载器
ClassLoader cl = Person.class.getClassLoader();
Class clazz4 = cl.loadClass("Person");//全类名
创建运行时类的对象
newInstance() 创建对应运行时对象,内部调用了运行时类的空参构造器(非private)
获取运行时类的属性结构及其内部结构
clazz调用
getDeclaredFields() 获取当前类所有属性
getTypeName() 获取类名
getDeclaredConstructors 获取当前类所有构造器
getDeclaredMethods 获取当前类所有方法
method调用
getAnnotations/getModifiers/getReturnType/getName/getParameterTypes/getExceptionTypes
获取父类以及带泛型父类
getSuperClass 获取父类
getGenericSuperClass 获取带泛型父类
获取接口
getInterfaces/getPackage/getAnnotations
调用运行时类中指定结构
属性
Class clazz = Class.forName("Person");//获取Person类实例
Person p = (Person) clazz.newInstance();//创建对象
Field name = clazz.getDeclaredField("name");//获取属性对象(属性名)
Field age = clazz.getDeclaredField("age");
name.setAccessible(true);//保证当前属性可访问
age.setAccessible(true);
name.set(p,"Origami");//设置属性值
age.set(p,19);
String n = (String) name.get(p);
Integer a = (Integer) age.get(p);
System.out.println("name = " + n + ", age = " + a);
方法
public String showName(String habit){
System.out.println("name = " + name + ", and my habit is " + habit);
return habit;
}
// ---------------------------------------------------------------------
Class clazz = Class.forName("Person");//获取Person类实例
Person p = (Person) clazz.newInstance();//创建类对象
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p,"Origami");
Method showName = clazz.getDeclaredMethod("showName",String.class);//获取方法对象(方法名,形参列表的类型)
showName.setAccessible(true);//保证可以访问
Object invoke = showName.invoke(p,"coding");//调用方法(调用者,实参)
//invoke 的返回值,即为方法的返回值
System.out.println(invoke);