java基础
常用数据类型
- 基本类型:
byte, short, int, long, float, double, boolean, char
- 引用类型:
String, 类
注:
- 整数常量默认int型
- 对long型常量末尾加
l
或L
- 浮点数常量默认double型
- 对float型常量末尾加
f
或F
- char赋值不能为空(String可以为””):
Error: char c = '';
- 大转小-位截断
流程控制
1 | // 条件 |
Java面向对象
4种访问权限修饰符
- private:私有的,只有类的内部可访问
- 缺省的:与private相比,多了同一个包。
- protected:保护的,与缺省的相比,多了一个不同包的子类
- public:公有的,整个工程。
范围:类内部<同一个包<不同包<工程范围(private<缺省<protected<public)。不同包可以创建相同的类不冲突。
修饰类的成员:变量、方法
类的修饰:缺省和public
包管理
- package关键字:申明java文件的包名或路径,”xxx.xxx.xxx”每一个“.”代表分了一层目录。同一个包下,不能有相同的接口和类
- import关键字:导包必须,显示导入类或接口
import static
导入类或接口中的静态结构:属性或方法
构造器
1 | [public | private | protected] classname (参数列表){ |
注:类有默认无参构造器,构造器可以重载,一旦显式提供构造器后,默认无参构造器不再有效
1 | // this 调用构造器 |
- 重载:更改同名函数的参数个数和类型实现重载。
继承(Inherit)
- 子类拥有父类的非private方法和属性
- 子类可以重写父类的方法
- 只能单继承
- 提高了类间的耦合性
- 子类使用super构造器调用父类结构:属性和方法。
- 所有类缺省继承lang包下的Object类。
- 同名变量的搜寻,就近原则
1 | class Person{ |
重写
在子类中对继承的类中的方法进行改造(重写)并覆盖。
- 重写的方法修饰符权限 >= 父类中被重写的方法权限
- 不能重写private修饰的父类方法
- 两者的返回值要么都是void或都相同,要么重写的方法返回值类型是父类中被重写方法的子类,基本类型必须相同。
- 子类重写的方法抛出的异常类型不大于父类中被重写的方法抛出的异常类型
- static方法不可被重写
同名属性优先使用子类属性,或使用super关键字调用父类属性。子类会默认调用父类的super()空参构造器,一个类的构造器首行只能是super()或this()。
子类对象实例化过程
- 子类继承父类后,获取了父类中所有的属性和方法。
- 子类创建过程中,直接或间接的通过super关键字调用了父类的构造器。
- 只是调用父类的构造器,并没有创建父类的实例
多态(Polymorphic)
- 继承
- 重写
- 父类引用指向子类对象
- (向上转型)使用多态方式调用方法时,首先检查父类中是否有该方法,没有则编译错误;如果有,再去调用子类的同名方法
- 运行时行为
- 虚方法,动态绑定,运行时才确定方法,晚绑定
多态的实现方式:
- 有继承
- 有重写
- 接口
- 抽象方法和抽象类
向上转型与向下转型(多态性)
1 | Father f = new Son(); // 向上转型,**子类结构已经加载到内存** |
- 向上转型:父类引用指向子类对象(这里的父类可以是接口),不能调用与父类不同的特有的子类方法。
- 向下转型:使用强制类型转换,可以调用子类的特有方法。
a instanceof B
: B与B的父类使其等价。
编译看左边,运行看右边。
Object类
- clone()方法:克隆对象
- equals()方法:判断对象的值是否相等
== 与 equals
== 运算符:
- 可用于基本数据类型和引用数据类型。
- 对于基本数据类型的比较,比较的是存储的值。
- 对于引用类型的数据,比较的是指向的地址值。
equals方法:
- 只适用于引用型数据类型
- 用于比较对象间的值是否相同,自定义类需要重写equals方法。(继承自Object,重写前等价于==)
装箱与拆箱
使基本数据类型具有类的特征。主要用于:基本数据类型-包装类-String,三者之间的转换
基本类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
装箱:
1 | int a = 12; |
拆箱:
1 | Integer i = new Integer(12); |
自动装箱与自动拆箱 (JDK 5.0+)
1 | int a = 12; |
- 自动装箱:
Integer a = 12;
- 自动拆箱:
int b = a;
- 转String转:valueof() 与 parseXXX()
String s = [int|double|float|byte] + "";
String s = String.valueOf([int|double|float|byte]) | String.valueOf(Object)
object o = Object.parseObj(String);
面试题:
1 | // 第一题: |
static修饰符
static:静态
- 静态:不随外部的变化而变化,被所有对象共有,属于类本身。
- 可以修饰:属性、方法、代码块、内部类
- 静态成员随着类的加载而加载
- 静态变量不仅可以通过类调用
- 由于类只加载一次,故静态变量在内存中只有一份,存在方法区的静态域中
- 静态方法中不能使用this、super、非静态变量。(生存周期的角度去理解)
单例模式
私有化构造器,使用类变量
1 | // 饿汉式: 提前新建好了,对象加载时间过长,但是线程安全 |
代码块
- 存在于类内部,属于类成员。用来初始化类、对象
- 只能用static修饰,或者不修饰
- 静态代码块属于类,非静态代码块属于对象
1 | main(){ |
赋值顺序:默认赋值->(显示赋值->代码块赋值)->构造器
final修饰符
- final修饰类: 不能被继承,不能被拓展,最终形态。
- final修饰方法:不能被重写,最终版本。
- final修饰变量:一旦被赋值,则不可修改。常量。
类中的final变量初始化:显式、构造器、代码块。即在对象出生前,要被初始化
抽象(Abstract)
如果一个类中没有包含足够的信息来描绘一个具体的对象,那这个类就是抽象的。
抽象方法
- 只属于抽象类:如果一个类包含抽象方法,那么该类必须是抽象类
- 非抽象类(具体类)必须重写:任何子类必须重写父类的所有抽象方法,或者声明自身为抽象类
- 没有方法体:声明为抽象方法时,不含方法体
抽象类
- 抽象类不能被实例化。只有抽象类的非抽象子类可以创建对象
- 抽象类不一定包含抽象方法,包含抽象方法的类一定是抽象类
- 可以是“模板类”,可以调用抽象方法。
匿名子类
1 | main(){ |
封装(Encapsulation)
- 良好的封装可以减少耦合
- 类内部的结构可以自由修改
- 对成员变量精确控制
- 隐藏信息,实现细节
接口(Interface)
接口与类是并列的结构。
接口与类的区别
- 接口不是类,没有构造器
- 接口中的所有方法必须是抽象方法
- JDK7.0-:只能有全局常量(public static final, 可省略)和抽象方法,JDK8.0+:增加了静态方法和默认方法
接口特性
- 接口中每一个方法被隐式指定且只能是
public abstract
- 接口中的变量会被隐式指定且只能是
public static final
- 接口中的方法不包含方法体,且只有两种结果:
- 被抽象类实现,可以不含方法体
- 被具体类实现,必须重写方法体
- 接口可以extends多继承多个接口
接口的多态性:
1 | main(){ |
总结:接口是方法的规范,面向接口编程。
与抽象类的区别
- 抽象类中的方法可以有方法体,但是接口中所有的方法不能有方法体
- 一个类只能继承一个抽象类,而一个类可以实现多个接口
- 接口没有构造器
匿名实现类
1 | // 向上转型,多态 |
总结:接口就是抽象方法的集合
代理模式(Proxy)
两个类实现同一个接口,代理类中有被代理类的对象,代理类代表被代理类的功能,屏蔽真实的对象
1 | public class ProxyPattern { |
工厂模式
实现了创建者与调用者的分离(开闭原则:对拓展开放,对修改封闭)
1 | // 工厂方法模式 |
JDK8.0+的接口新特性
接口中的成员:
- 全局常量
- 抽象方法
- 静态方法(默认public,8.0+)
- 默认方法(默认public,8.0+)
1 | interface Test{ |
- 静态方法:只能使用接口调用
- 默认方法:可以重写方法体,也可以不重写使用默认的方法体。
注:父类与接口中的方法同名同参数,那么子类在没有重写的情况下默认调用父类方法。(类优先,若是同名属性则不被允许,多个接口中的同名默认方法冲突否则必须重写。若要调接口中的方法—InterfaceA.super.method
)
内部类(不常见)
- 局部内部类:方法体内、代码块内。
- 成员内部类:类的下一层,与成员变量平级
成员内部类
- 可以调用外部类的结构
- 可以被static修饰
- 可以有4种修饰方式
局部内部类
- 接口实现类
- 匿名实现类。
- 局部内部类调用外部的局部变量,要求其是final的。
面向对象总结
- abstract:可以修饰类和方法,类是抽象的不能被实例化,方法是抽象的没有方法体需要具体类实现
- 接口相互之间可以继承,抽象类可以实现接口,抽象类可以继承非抽象类
3.抽象与接口体现了多态
异常
- 编译时异常
- 运行时异常
异常体系结构
1 | java.lang.Throwable |
异常处理方式
try-catch-catch-...-finally
:1
2
3
4
5
6
7
8
9try{
// 可能会出现异常的代码
}catch(异常类型1 变量1){
// 发生异常1时,处理异常的代码
}catch(异常类型2 变量2){
// 发生异常2时,处理异常的代码
}finally{
// 无论对错,始终一定都会执行的代码,即使catch里有return语句,通常用于关闭资源
}异常子类的catch必须在异常父类的上面,否则报错。
- 异常类型方法:
- getMessage(): 返回一个String
- printStackTrace(): void方法,输出栈追踪
method() throws 异常类型
: 不处理异常,将异常抛给调用者,表示该方法可能抛出一个异常throw new [异常类型1,2...]
:抛出异常- 子类重写父类方法抛出的异常只能是父类异常的子异常。
自定义异常
- 继承已有的异常类
- 提供全局常量标识类
- 提供多个构造器
多线程
- 程序:实体
- 进程:运行中的程序,是资源分配的基本单位
- 线程:进程的细分,可理解为执行路径,是调度的基本单位
- 并行:多个CPU同时执行多个任务,CPU的并行
- 并发:一个CPU同时执行多个任务,任务的并发
- 一个Java程序至少有3个线程:main,垃圾收集,异常处理
通过Thread类创建
- 继承java.lang.Thread类
- 重写run方法,是线程执行的代码
- 创建Thread类的子类对象
- 通过对象调用start方法
- start方法只能被同一个线程调用一次(IllegalThreadStateException)
- 使用匿名子类使用不同的run方法
Thread常用方法
- start方法:启动线程
- run方法:线程的执行内容
- currentThread方法:获取执行当前代码的线程
- setName、getName方法:命名的修改与获取
- yield方法:释放对CPU的占用,回到就绪状态
- join方法:阻塞执行当前代码的线程,等待调用方法的线程执行完毕后,结束阻塞状态
- stop方法:以及弃用,结束线程的生命周期
- sleep方法(静态):当前线程“睡眠”毫秒数(1秒=1000毫秒),使线程阻塞
- isAlive方法:线程是否存活
线程的调度
- 时间片
- 优先级
优先级:从概率上来讲优先
1 | /** |
通过Runnable接口创建
- 创建实现Runnable接口的类
- 实现抽象方法:run
- 创建该类的对象
- 将此对象传入Thread的构造器中,创建Thread的对象
- 通过Thread的对象调用start方法
继承Thread类影响了拓展性,而实现Runnable接口则不会影响拓展性
两者对比
1 | // WindowsTest继承了Thread类,创建3个对象来建立3个线程 |
- Thread也重写了Runnable接口
- 第二种方式相当于覆盖了Thread的run方法
线程的状态
- 新建
- 就绪
- 运行
- 阻塞
- 死亡
线程状态转换图:
线程的同步
问题:
- 当线程之间存在共享数据时,就会存在安全问题
- 共享数据成为临界资源,访问临界资源的代码称为临界区
如何解决:
- 对线程的访问的资源进行加锁,加锁后其他进程不能访问
java的解决方式:
synchronized
关键字:1
2
3
4
5
6
7
8
9
10
11
12
13// 同步代码块
synchronized(同步监视器){
// 临界区
}
// 同步方法:Runnable实现类-this为同步监视器
权限修饰符 synchronized void method(){
// 临界区
}
// 同步方法:Thread子类-ClassName.class为调用者,且只有一个
权限修饰符 static synchronized void method(){
// 临界区
}同步监视器:锁-任何一个对象都可以作为锁;要求-多个线程必须共用一把锁
- this作为锁:适用于实现Runnable接口的类的对象,因为只创建了一个对象
- ClassName.class作为锁:该对象只会加载一次
- 在Runnable实现类中考虑使用this为锁,在Thread的子类中考虑使用ClassName.class作为锁
同步的缺点:
- 使用了线程同步相当于,线程的串行执行,速度慢
线程安全的单例模式
懒汉式:线程安全
1
2
3
4
5
6
7
8
9
10
11
12class LazySingleton{
private static LazySingleton s = null;
private LazySingleton() {}
public static synchronized LazySingleton(){
if(s == null){
s = new LazySingleton();
}
return s;
}
}懒汉式:双重校验锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class LazySingleton{
private static LazySingleton s = null;
private LazySingle() {}
public static LazySingleton getInstance(){
if(s == null){
synchronized(LazySingleton.class){
if(s == null){
s = new LazySingleton();
}
}
}
return s;
}
}
死锁
不同的线程分别占用对方的资源不放弃,都在等待对方去释放自己需要的同步资源,就形成了死锁。可见申请资源的方向称为一个环状结构
1 | // 线程A |
Lock锁
- Lock对象:JDK5.0+,作为同步锁
java.util.concurrent.locks.Lock
接口- ReentrantLock类实现了Lock接口
synchronized与lock的不同:
- synchronized相当于自动锁,自动加锁,自动释放锁;lock手动启动锁,手动释放锁
- 优先使用顺序:lock锁->同步代码块->同步方法
线程通信
- wait方法:阻塞当前线程,并释放锁
- notify方法:唤醒用wait阻塞的线程
- notifyAll方法:唤醒所有wait阻塞的线程
- 方法的调用者必须是同步代码块或同步方法的同步监视器,否则出现异常
IllegalMonitorStateException
- 3个方法都来自Object,因为同步监视器可以是任何对象,他们的共同父类是Object
生产者消费者问题:使用wait与notify让生产者与消费者之间产生交互
1 | package ind.MultiProcess; |
JDK5.0+新增方式
- 实现Callable接口
- 使用线程池
Callable接口:
- 创建Callable接口的实现类
- 借助FutureTask类创建对象,FutureTask类实现了Runnable接口
- 用FutureTask类的get方法获取线程执行call的返回值
- 同样将FutureTask类的对象来构造Thread,来启动线程
1 | public class CallableTest { |
优点:
- 有返回值
- 可以抛出异常
- 支持泛型
使用线程池:
- 提高响应速度
- 降低资源消耗
- 便于线程管理
ExecutorService:线程池接口
1
2
3
4
5
6
7ExecutorService service = new ExecutorService(10); // 创建10个线程的线程池
void execute(Runnable command): 执行任务,Runnable接口的实现类
void shutdown(): 关闭连接池
=========================
corePoolSize: 核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时,最多保持多少时间后会终止Executors:线程池的工厂类、工具类。静态方法
Java常用类
String类
- final类
- 实现了
java.io.Serializable, Comparable<String>, CharSequence
三个接口Serializable
接口:表示支持序列化Compareable<String>
接口:比较大小
final char[]
:用于存储字符串数据不可变的字符序列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26String s1 = "123"; // 字面量,字符串值存储在字符串常量池中,其中不会存储相同的常量字符串
String s2 = "123";
String s3 = new Stirng("123") // 存储在堆中
sysout(s1 == s2) // true
sysout(s1 == s3) // false
Person p1 = new Person("Jack", 12)
Person p2 = new Person("Jack", 113)
sysout(p1.name == p2.name) // true 因为"Jack"为字面量,存储在常量池中
String s1 = "javaEE";
String s2 = "hadoop";
String s3 = "javaEEhadoop";
String s4 = "javaEE" + "hadoop";
String s5 = s1 + "hadoop";
String s6 = "javaEE" + s2;
System.out.println(s3 == s4); // true,常量与常量的拼接都在常量池中
System.out.println(s3 == s5); // false
System.out.println(s3 == s6); // false
System.out.println(s5 == s6); // false
// 只要其中一个是变量,结果就在堆当中
String s7 = s5.intern() // 返回在常量池中的字符串
sysout(s3 == s7) // true注意事项:
- 拼接操作会产生新的字符串常量
- replace操作也会产生新的字符串常量
JVM内存结构:
String常用方法
- int length():返回字符串长度
- char charAt(int index):返回某索引处的字符
- boolean isEmpty():判断字符串是否为空
- String toLowerCase()
- String toUpperCase()
- String trim():返回字符串的副本,忽略前后空格
- boolean equals(Object obj):比较字符串的内容是否相同
- String concat(String str):等价于“+”
- int CompareTo(String anotherString):比较字符串中每个字符的Ascii大小
- String substring(int beginIndex)
- boolean startWith(String suffix)
- boolean endWith(String prifix)
- boolean contains(CharSequence s)
- int indexOf(String str)
- int lastIndexOf(String str)
- String replace(String oldStr, String newStr)
- String replaceAll(String regex, String replacement)
- String replaceFirst(String regex, String replacement)
- boolean matches(String regex)
- String split(String regex)
- String转其他类型:parseXxx(String str)
- 其他类型转String:String.valueOf(Xxx)
- String转为char数组:str.toCharArray()
- char数组转为String:调用String的构造器
- String转为byte数组:str.getBytes(String charset),使用默认字符集
- byte数组转为String:调用String的构造器,使用编辑器默认字符集,或者利用参数指定字符集
StringBuffer(效率低)与StringBuilder(效率最高)
- String:不可变的字符序列;使用char数组存储数据
StringBuffer:可变的字符序列,线程安全的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23StringBuffer sb = new StringBuffer(); // 底层创建一个长度为16的char数组
sb.append("hello") // 数组的扩容
/*
底层是将 原字符串的长度*2+2 作为新字符串的长度,同时赋给新的数组copyof
*/
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity));
}
}
private int newCapacity(int minCapacity) {
// overflow-conscious code
int newCapacity = (value.length << 1) + 2;
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
? hugeCapacity(minCapacity)
: newCapacity;
}StringBuilder:可变的字符序列,线程不安全的。jdk5.0+;底层原理同StringBuffer,两者继承自同一父类。
常用方法:
- append
- replace
- insert
- reverse
- setCharAt
- delete
日期和时间
时间
- System类中的
currentTimeMillis()
:返回当前的时间戳毫秒数,返回long型
日期
- java.util.Date
- java.sql.Date:继承自前者
转换:利用时间戳来相互转换
SimpleDateFormat格式化日期
1 | Date date = new Date(); |
Calender抽象类的使用 JDK8.0-
1 | Calendar calendar = Calendar.getInstance(); |
JDK8.0+ 时间API
常用类(没有偏移量了;不可变性):
- LocalDate
- LocalTime
- LocalDateTime
- Instant
- DateTimeFomatter
方法:
- withxxx():返回带有指定值的新的日期对象
- plusxxx()
- minusxxx()
比较器
两个接口
- Compareable(内比较器):实现类需重写
compareTo
方法 - Comparator(外比较器):定制排序,重写
compare
方法
System、Math、BigInteger、BigDecimal
- System:有关系统的
- Math:有关数学运算的
- BigInteger:表示不可变的任意精度的整数
- BigDecimal:表示不可变的、任意精度的有符号十进制定点数。精度较高。
枚举类
有限个、确定的值的类。(JDK5-自定义,JDK5+引入enum关键字)
自定义枚举类
- 私有化每个枚举值
- 私有化构造器
- 创建全局枚举对象
1 | class Season{ |
使用enum关键字
- 继承自java.lang.Enum
1 | enum SeasonStatus{ |
枚举类的方法:
- values()
- valueOf(String objName)
枚举类实现接口:
- 同类一样实现接口方法
- 每个对象可以不同地实现接口方法
注解(Annotation)JDK5.0+
- @override:限定重写父类的方法
- @Deprecated:过时的
- @SuppressWarnings:抑制编译器警告
自定义注解:参照@SuppressWarnings;有成员value,或无成员
1 | public SuppressWarnings { |
元注解
对现有的注解进行解释说明的注解:
- Retention(保留):用于指定注解的生命周期-SOURCE\CLASS(default)\RUNTIME
- Target:指定注解能用于修饰那些元素-TYPE/FIELD/METHOD/CONSTRUCTOR… …
- Documented:令注解在被javadoc解析后被保留下来
- Inherited:令注解具有继承性
JDK8.0+新特性
- 可重复注解
- 类型注解:TYPE_USER/TYPE_PARAMETER
集合
- Collection接口:单列数据,单一对象
- List:有序、可重复
- Set:无序、不可重复
- Map接口:双列数据,存储key-value
- HashMap
- LinkedHashMap
- TreeMap
- HashTable
- Properties
- Collections工具类
Collection接口常用方法:
- add(Object e):添加一个元素
- addAll(Collection coll):添加一个集合中的所有元素
- size():集合的大小
- isEmpty():集合是否为空
- clear():清空集合
- contains(Object e):是否存在元素,使用equals来比较元素
- containsAll(Collection coll):判断集合的元素是否都存在
- remove(Object e)
- removeAll(Collection coll)
- retainAll(Collection coll1):求两个集合的交集,并替换this集合
- Object.equals()
- Object.hashCode()
- toArray():集合—>Object数组
- Arrays.asList():数组—>集合
- iterator():放回Iterator接口的实例
注:添加的obj要重写equals方法
Iterator接口
迭代器,用于遍历集合中的元素,只能用一次。不可逆。
- E next():指针下移
- boolean hasNext():指针不变
- void remove():删除集合中的元素()
1 | // 常用遍历 |
List接口(Collection子接口)
元素有序、可重复、动态变化大小。List实现类:
- ArrayList:线程不安全,效率高;底层用Object[]数组
- LinkedList:插入、删除效率高;底层使用双向链表实现
- Vector:线程安全,效率低;底层用Object[]数组
ArrayList
JDK7.0:
1 | ArrayList arr = new ArrayList(); // 容量默认为10的Object数组 |
JDK8.0+:
1 | public ArrayList() { |
LinkedList
1 | transient Node<E> first; |
Vector
基本不用了
List接口常用方法
- void add(int index, Object ele)
- boolean addAll(int index, Collection eles)
- Object get(int index)
- int indexOf(Object obj):如果不存在返回-1
- int lastIndexOf(Object obj)
- Object remove(int index) / boolean remove(Object o):按照索引删除,并返回此元素
- Object set(int index, Object ele),按照索引修改,并返回此元素
- List subList(int fromIndex, int toList)
Set接口(Collection子接口)
- 存储无序的:不等于随机性,体现在哈希值上,根据哈希值存储。
- 不可重复的:利用hashCode方法与equals方法判断是否重复。底层:数组+链表,哈希值与equals方法,在数组中的位置与哈希值有关(JDK7与8不一样,对链表的操作分为头插法8+与尾插法7-)
实现类:
HashSet
:主要实现类,线程不安全,可以存储null值,底层是一个数组,长度为16LinkedHashSet
:HashSet的子类,可以按照顺序变量。原理依然是数组+链表,不过链表的节点的双向量表,记录的是添加的顺序。遍历的效率高
TreeSet
:空参构造底层是一个TreeMap,可以对属性排序。只能添加同类型的数据,并自然排序(实现Comparable接口的类)。根据compareTo来判断对象是否相同(返回0)。- 定制排序:
TreeSet treeset = new TreeSet(Comparator com)
,使用比较器
- 定制排序:
所以一般需要重写hashCode方法与equals方法:相等的对象要有相等的哈希值。
1 | // 面试题:理解hashSet利用哈希值存储、不可重复性的特点 |
Map接口(Collection子接口)
双列数据,key-value型数据。实现类
- HashMap:常用实现类。线程不安全的。有null的key与value
- LinkedHashMap:继承自HashMap,可以按照添加的顺序遍历
- TreeMap:可以按照添加的顺序排序(按照key排序,要求是同一个类)遍历,底层使用红黑树
- Hashtable:古老类。线程安全。不能有null的key与value
- Properties:用来处理配置文件,key与value都是String型的
HashMap
底层概述:
- JDK7-:数组+链表
- JDK8+:数组+链表+红黑树
- Key:无序,不重复。使用set存储Key,需要重写equals方法与hashCode方法
- Value:无序,可重复。
- Key-Value:一个Entry
JDK7:Entry
1 | HashMap hashMap = new HashMap(); // 默认16长度的Entry数组,数组+链表 |
JDK8:Node
1 | HashMap hashMap = new HashMap(); // 底层没有创建数组,Node,数组+链表+红黑树 |
LinkedHashMap
内部单元为Entry,继承自HashMap中的Node
1 | static class Entry<K,V> extends HashMap.Node<K,V> { |
Map中的常用方法
- Object put(Object key, object value)
- void putAll(Map m)
- Object remove(Object key)
- void clear()
- Object get(Object key)
- boolean containsKey(Object key)
- boolean containsValue(Object value)
- int size()
- boolean isEmpty()
- boolean equals(Object obj)
通过迭代器遍历键与值
- Set keySet()
- Collection values()
- Set entrySet()
Properties(继承自Hashtable)
key和value都是String,通常用于配置文件xxx.properties
。使用文件流操作。
Collections工具类
常用方法:
- reverse(List)
- shuffle(List)
- sort(List)
- sort(List, Comparator)
- swap(List, int, int)
- Object max(Collection)
- Object max(Collection, Comparator)
- int frequency(Collection, Object)
- void copy(List destination, List src):
Arrays.toList(new Object[src.size()])
- boolean replaceAll(List, oldVal, newVal)
- synchronizedXxx()
泛型
类型泛化,默认为Objec类型
使用
1 | ArrayList<Integer> arr = new ArrayList<>(); |
自定义
1 | public Class MyObject<T>{ |
注意点:
- 多个泛型参数用逗号隔开
- 构造器不需要带泛型声明
- 不同泛型引用不能相互赋值
- 静态方法中不能使用类的泛型
- 不能使用new T()
- 静态方法可以是泛型方法
泛型方法:具有泛型的结构,与类的泛型没有任何关系
1 | // <E> 说明了他是泛型方法 |
通配符?
的使用
1 | List<String> = new List<>(); |
有限制的通配符:
- <? extends SuperClass>: SuperClass的子类或本身才可以, 往下找,add(Object)
- <? super SuperClass>:SuperClass的父类或本身才可以,往上找,add(Person往下)
文件
File类
不涉及到文件内容,仅存储文件的路径
1 | File file1 = new File("hello.txt"); // 相对于当前的项目路径下 |
常用方法:
- public String getAbsolutePath()
- public String getPath()
- public String getName()
- public String getParent()
- public long length()
- public long lastModified()
- boolean renameTo(File2 file2)
- String[] list():返回路径下的文件名
- File[] listFile():返回文件对象
- boolean isDirectory()
- boolean isFile()
- boolean exists()
- boolean canRead()
- boolean canWrite()
- boolean isHidden()
IO流
- 字节流:
OutputStream
,InputStream
- 字符流:
Writer
,Reader
- 转换流:
InputStreamReader
,OutputStreamWriter
字符流读取:
1 | public static void main(String[] args){ |
字节流使用:
1 | public static void streamTest(){ |
转换流:
- 将字节流转为字符流。—-解码过程
- 再将字符流转为字节流。—-编码过程
标准输入输出流:System.in
与System.out
打印流:PrintStream
数据流:DataInputStream
对象流(序列化与反序列化)
ObjectInputStream
ObjectOutputStream
1 | ObjectOutputStream oos = null; |
自定义类可序列化的要求:
- 实现
java.io.Serializable
接口 - 定义序列版本号属性:
private static final long serialVersionUID = 10L;
:解决序列化与反序列化过程中,版本不一致的问题 - 其内部所有属性也必须是可序列化的
随机存取文件流(RandomAccessFile类)
网络编程
服务器端例子:
1 | public class TCPServer { |
客户端例子:
1 | public class TCPClient { |
反射(动态性)
反射的主要且重要的特点:动态性。运行前存在变数,运行后才确定结果。
反射示例
1 | public class ReflectionTest { |
类的加载过程
- javac命令编译java源文件,生成字节码文件(.class结尾)
- 使用java命令对某个字节码文件解释执行。相当于将某个类文件加载打内存中去。称为运行类,并作为Class类的一个实例。
如何获取Class实例
Class<Person> clazz = Person.class;
1
2Person p = new Person();
Class clazz = p.getClass();Class clazz = Class.forName("ind.Reflection.Person");
1
2ClassLoader classLoader = ClassLoaderDemo.class.getClassLoader();
Class clazz3 = classLoader.loadClass("ind.Reflection.Person");
加载过程(类加载器ClassLoader):
- 加载:将class字节文件加载到内存中去,将静态数据转换为方法区的运行时数据结构
- 链接:为类变量分配内存,将常量替换为地址引用
- 初始化:执行类构造器\
()方法的过程
加载器:
- 系统类加载器AppClassLoader
- 拓展类加载器ExtClassLoader
- 引导类加载器(无法获取)
使用类加载器加载配置文件
1 | public static void testA() throws IOException { |