文章目录
- Java基础知识点
- 1. 初识Java
- 2. 环境配置
- 3. 常量&变量
- 4. 运算符
- 5. 选择结构和循环结构
- 6. 循环
- 7. 数组
- 8. 方法
- 9. 可变参数&API
- 10. 类&对象
- 11. 封装
- 12. 继承
- 13. 抽象类和接口
- 14. 多态
- 15. 枚举和常用类
- 16. 常用类&异常
- 17. 异常&多线程
- 18. 线程&数据结构
- 19. 集合List和Set
- 20. Map
- 21. File
- 22. IO流
- 23. Lambda & Stream流
- 24. 单元测试&jar包&资源文件&设计模式
- 25. JavaBean&反射&注解
Java基础知识点
知识点是自己整理的,适合初学者,手打有些可能出现些许错误,还请多多包涵并指出
1. 初识Java
2. 环境配置
3. 常量&变量
常量
一个固定不变的值
整数型、浮点型、字符型、布尔型、字符串型
变量
就是一个用于存放数据的容器,一次只能存储一个值
语法1:先声明,再赋值
int i ;
i = 10;
语法2:声明时候直接赋值
int i = 10;
注意点:赋值的时候,值类型要和变量的类型对应
变量的分类:
成员变量:定义在类里面,和方法平级
被static修饰,所有方法都可以使用
有默认值
局部变量:定义在方法或代码块内
没有默认值,需要赋值才能使用
类型转换
小转大自动转换
大转小强转(强转可能出现精度损失)
char、short、byte不可以相互转换
4. 运算符
1.数学运算
+:用于数学运算就是正常加法;用于拼接字符串,一旦以字符串开头的拼接,不再进行数学运算
-:就是正常的减法
*:乘法
/:注意参与运算的数据类型是否涉及浮点类型
%:取模(就是数学中的取余)
2. 自增自减
++、–在前:先运算后使用
++、–在后:先使用后运算
3. 赋值运算
i += 1 ==> i = i+1
i -= 1 ==> i = i-1
i *= 1 ==> i = i *1
i /=1 ==> i = i/1
i %=1 ==> i = i%1
4. 比较运算
<、>、<=、>=、!=、==
5. 三目运算
x?y:z
6. 逻辑运算
&、|、!、&&、||
&&和||具有短路效果
5. 选择结构和循环结构
1. 选择结构
a:if语句
单条件:if(判断语句){
执行语句;
}
双条件:if(判断语句){
执行语句;
}else{
执行语句;
}
多条件:if(判断语句){
执行语句;
}else if(判断语句){
执行语句;
}…
b: switch语句
switch(值){
case 值1:
执行语句;
break;
case 值2:
执行语句;
break;
case 值3:
执行语句;
break;
…
default:
执行语句;
}
2. 循环结构
a:while
while(判断语句){
执行语句;
}
6. 循环
1. do…while
语法:do{
循环语句;
}while(判断语句);
2. for
语法:for(初始化语句;判断语句;循环执行后语句){
循环语句;
}
ps:嵌套循环
多层循环嵌套使用;外层循环执行一次,内层循环执行一遍。
3. 控制语句
break:停止当前循环
return:停止当前方法
continue:跳过当前,继续执行
7. 数组
1. 数组
概念:数组就是多个相同数据类型的数据
语法:
静态:
数据类型[] 变量名 = {元素1,元素2,元素3,…};
动态:
数据类型[] 变量名 = new 数据类型[长度];
数组索引:索引从0开始,最后一位为长度-1;
数组遍历:
普通for:需要索引的操作,只能使用普通for
增强for:不需要索引的操作,两者皆可
内存图:
8. 方法
1. 语法:
修饰符 返回值类型 方法名(形参){
//功能体;
}
例如:
public static void add(){
}
public static void add(int a){
}
public static void add(int a,int b){
}
2. 方法的调用
-
类名.方法名():被调用的方法必须有static修饰
_01Test.method1(c, a);
-
对象.方法名():被调用的方法没有被static修饰
-
方法名():被调用的方法与调用者的要么都有static修饰,要么都没有static修饰
max(a, b,c);
注意事项:
-
实际参数必须要一一对应【类型,个数,顺序】;
2. 返回类型不是void,方法里面必须要有return; 3. 一般return后面不要写功能语句;
3. 方法重载
在同一个类中,方法名一致,形参列表不同【个数,顺序,类型,和返回值类型以及形参的名字无关】
9. 可变参数&API
1. 可变参数***【重点掌握】***
语法:数据类型… 变量名
例如:
public static void add(int... a){}
本质:
可变参数的本质就是数组;
注意事项:
1. 可变参数只能写在方法里面的形参里面
2. 一个方法只能有一个可变参数,并且只能写在形参的最后一位
3. 调用可变参数的方法,实参可以是任意个
2. 参数传递
-
基本数据类型,传递的就是值本身 -----------------值传递 (实参的最终值不会受到方法内的操作而改变)
-
引用数据类型,传递的是数据的地址值 --------------引用传递(实参的地址值在方法操作前后不会发生改变,但是数据会发生改变)
PS:主要是与堆栈内存储结构有关,可以换种理解方式:借钱;
3. 查询API
10. 类&对象
1. 面向对象
面向对象是一种编程思想,就是把问题拆分成了一个一个对象,并且赋予特定的属性和方法,然后通过对象去解决一个问题
2. 类
类是具备某些共同特征的实体的集合,是对所具有相同特征实体的抽象
3. 对象
对象就是类的实例
通常会将对象划分成两个部分,即动态部分(方法)和静态部分(属性----->成员方法)
4. 构造方法
作用:
用于创建类的对象
语法:
无参构造:
修饰符 类名(){
方法体;
}
带参构造:
修饰符 类名(参数列表){
方法体;
}
例如:
public class Student {
String name;
int age;
public Student() {
System.out.println("无参构造方法执行了......");
}
public Student(String name,int age) {
this.name = name;
this.age = age;
System.out.println("带参构造方法执行了......");
}
}
特点:
1. 每个类中至少有一个构造方法
2. 如果没有显示构造方法,那么存在一个隐式的无参构造方法
3. 如果一个类中有显示的构造方法,那么隐式的无参构造方法就不再存在
11. 封装
1. 包的概念【package】
在编写代码的时候如果要使用非当前包下面的类,需要导包
导包的快捷键Ctrl+Shift+O
2. 权限修饰符
Java中提供三种权限修饰符,产生四种访问修饰权限
public | protected | default | private | |
---|---|---|---|---|
同一类中 | √ | √ | √ | √ |
同一个包子类、其他类 | √ | √ | √ | |
不同包子类 | √ | √ | ||
不同包其他类 | √ |
3. 封装:
-
私有化成员变量【private修饰】
- 对成员变量提供setter&getter方法,达到数据取值和赋值的操作
- 必须提供公共的无参构造
- 类必须是public修饰
注意:
如果成员变量的数据类型为Boolean类型,那么getter方法需要更名为isXxx方法
使用快捷键一件生成getter与setter方法
4. this
-
用于区分成员变量与局部变量之间的二义性
【这里的this就是一个对象,this所在的方法被谁调用,this就是谁】
- this用于同一个类中构造方法之间的相互调用.
public User() { this("张三","123456"); } public User(String username,String password) { this.username = username; this.password = password; }
PS:this实现构造方法之间的调用只能写在构造方法的第一句;必须要有出口!
12. 继承
1. 继承
语法:
public class 子类类名 extends 父类类名{
}
可以继承的内容:
-
非私有化的成员变量
2. 非私有化的方法
继承的注意事项:
1. Java里面只能支持单继承【一个类只能有一个直接父类】
2. Java里面支持多层级继承
2. 方法的重写【覆写】 Override
重写是因为父类的方法不能满足子类的需求
@Override 方法重写注解,为了校验当前的方法是否是方法重写
重写的语句:
-
子类方法签名【方法名+形参列表】 必须和父类方法一致
2. 子类方法的修饰符权限大于等于父类方法的修饰符权限 3. 子类方法的返回值类型必须和父类方法的返回值类型一致或是其子类
PS:方法重写过后的方法的调用顺序:就近原则,先在当前类中调用,如果当前类没有再去其父类中调用
3. Super
-
代表父类对象【但是不持有父类对象的地址】,在子类的方法里面调用父类的成员【方法、属性】
- 在子类的构造方法里面调用父类的构造方法【无论是否显式书写super()都在调用父类的构造方法】
13. 抽象类和接口
1. 抽象类
a. 语法:
public abstract class 类名{
//成员方法
//构造方法【不能用于创建实例对象】
//普通方法
//抽象方法【由abstract修饰,没有方法体】
}
b. 注意事项:
1. 抽象类不能实例化
2. 子类继承抽象类必须重写抽象类的抽象方法
2. Object类常用方法
a. toString方法
如果需要直接输出对象打印对象的数据,而不是地址值,就需要去重写Object类中的toString方法
b. equals方法
业内普遍认为对象内所有数据都一致的情况下,那么这两个对象就是相等的
equals在未重写的情况下,默认是==进行判断的,也就是两个对象的地址值进行比较;所以需要对equals方法进行重写,从而对两个 对象内的数据进行判断
c. hashcode方法
是一种算法,用于计算对象存储的地址值;一般情况下,不同的对象,地址值不同
3. 接口
a. 语法:
public interface 接口名{
//1.成员变量 默认由public static final 修饰
//2.抽象方法
//3.静态方法 由static修饰的方法
//4.默认方法 由default修饰的方法
}
b. 注意事项:
1. 接口不能直接实例化
2. 接口和接口之间支持继承,并且支持多继承
3. 接口与类之间可以多实现
4. 一个类可以同时继承并且实现接口【先继承再实现】
c. 类、抽象类、接口对比
构造方法 | 能否创建对象 | 成员变量 | 方法 | 抽象方法 | |
---|---|---|---|---|---|
类 | 有 | 能 | 有 | 有 | 没有 |
抽象类 | 有【但是不能创建对象】 | 不能 | 有 | 有 | 有 |
接口 | 没有 | 不能 | 有【默认由public static final修饰】 | 没有 | 有 |
14. 多态
1. 多态
父类类型接收子类对象
接口接收实现类对象
a. 强制转换语法:
子类类型 变量名 = (子类类型)父类类型;
实现类对象 变量名 = (实现类对象 )接口类型的对象;
b. 注意事项:
不能直接使用子类特有的属性和方法,要用–>强转
2. static
可以修饰的内容:成员变量、方法、内部类
被修饰的成员变量被所有对象所共享,最终值以最后一次调用修改为准
被修饰的方法会优化调用方式,调用方式为:类名.方法名
3. 内部类、匿名内部类
内部类就是在类的内部直接定义一个类
匿名内部类:一般就是使用一次接口的实现类对象【后期会使用lambda表达式来优化】
4. final
可以修饰的内容:
类:类不可以被继承
方法:方法不可以被覆写
成员变量:必须定义初始值,值不可以更改,一般情况和static一起使用,用来定义全局常量
局部变量:只能被赋值一次,便不能再被更改
15. 枚举和常用类
1. 代码块
静态代码块
书写在类中
语法:
static{
//执行语句
}
作用:在类加载的同时执行,只会被加载一次,所以内部执行代码只会执行一次
2. 枚举
a. 语法
public enum 枚举名{
对象1,对象2,.....对象n;
可以有的内容:
成员变量
构造方法【默认由private修饰,外部不能创建对象】
方法
}
b. 作用
一般用于取值比较固定的情况
3. eclipse快捷键
alt + / 代码提示
ctrl + alt + 方向键上 向上复制【有可能会有冲突】
ctrl + alt + 方向键下 向下复制【有可能会有冲突】
ctrl + / 单行注释/取消注释
ctrl + shift + / 多行注释
ctrl + shift + \ 多行取消注释
alt + shift + j 文档注释
alt + shift + a 批量操作【成员变量等】
ctrl + shift + o 导包或者删除多余的包
ctrl + shift + 回车 向上插入一行
shift + 回车 向下插入一行
ctrl + d; 删除选中行
alt + 方向键上 代码向上移动
alt + 方向键下 代码向下移动
ctrl + 鼠标左键 代码跟踪
alt + 方向键左 跟踪上一个编辑处
alt + 方向键右 跟踪下一个编辑处
ctrl + shift + f 代码格式化
ctrl + shift + t 查询jdk自带的类和自定义的类
ctrl + shift + r 查询自定义的类
ctrl + o 快速查看方法
ctrl + f 查找指定单词(可以替换指定的单词)
ctrl + shift + x 将选中的全大写
ctrl + shift + y 将选中的全小写
ctrl + shift + 双击L 重新设置快捷键
alt + shift + s Generate Constructor using Field 构造方法、toString、hashCode和equals
alt + shift + s Generate Getters And Setters getter/setter
4. 常用类
1. 包装类
a. 基本数据类型不具备对象的特征,所以jdk提供了基本类型的包装类型
b. 包装类除了可以表示某一个具体的值外,还提供了额外的属性与操作数据的方法
c. int的默认值是0,Integer默认值是null
d. 基本数据类型不能调用方法
f. 即使是包装类,比较两个数据是否相等最好还是使用equals方法
2. String
常用方法
char charAt(int index) 返回指定索引处的 char 值。
boolean endsWith(String suffix) 测试此字符串是否以指定的后缀结束。
int indexOf(int ch) 返回指定字符在此字符串中第一次出现处的索引。
boolean isEmpty() 当且仅当 length() 为 0 时返回 true。
String replace(char oldChar, char newChar) 返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
String[] split(String regex) 根据给定正则表达式的匹配拆分此字符串。
String substring(int beginIndex) 返回一个新的字符串,它是此字符串的一个子字符串。
String substring(int beginIndex, int endIndex) 返回一个新字符串,它是此字符串的一个子字符串。
char[] toCharArray() 将此字符串转换为一个新的字符数组。
String trim() 返回字符串的副本,忽略前导空白和尾部空白。
PS:String比较的时候也要是用equals
16. 常用类&异常
1. String、StringBuffer、StringBuilder
a. String:
String类是不可变类,一旦一个String对象被创建以后,它的字符序列是不能被改变的,直到这个对象被销毁
b. StringBuffer
StringBuffer对象是一个线程安全并且字符序列可变的字符串,当一个StringBuffer对象被创建以后,可以通过append、insert、 reverse、setCharAt等方法可以实现字符序列的改变,最终形成想要的字符串对象,通过toString转换成一个String对象
c. StringBuilder
StringBuilder用法和StringBuffer基本相同,只是StringBuffer是线程安全的类,StringBuilder是线程不安全的类
2. Math、BigInteger、BigDecimal
Math类中提供了大量的数学中的基本公式方法来对数字进行操作
BigInteger类可以表示比long类型更大的值
BigDecimal类可以精确的表示一个浮点数
3. System
常用方法:
数组的指定拷贝
static void arraycopy(Object src,int srcPos, Object dest, int destPos, int length)
//src:数据源
//srcPos:开始位置
//dest:复制的目标数组
//destPos:目标数组的起始位置
//length:复制的长度
4. Random、ThreadLocalRandom、UUID
a. Random
随机数生成类
int nextInt(int num):表示随机生成一个随机数,范围为[0,num)
b. ThreadLocalRandom
Random的子类
int nextInt(int num1,int num2):表示随机生成一个随机数,范围为[num1,num2)
c. UUID
一个表示不可变的通用的唯一表示符的类
static UUID randomUUID(); 返回一个唯一的随机数的值
5. Date
Java中表示时间的类 :Date(用法很简单)
常用时间操作类:
|—DateFormat
|—SimpleDateFormat
时间的格式化:
SimpleDateFormat sm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String strDate = sm.format(new Date());
6. Exception
异常的处理方式:
方式一:
捕获异常:让程序在在即使发生异常的情况下也能继续运行下去
语法:
try{
//可能发生异常的代码
}catch(异常的类型 变量名){
//处理异常的代码
}
17. 异常&多线程
1. 异常
a. 异常的处理方式
-
捕获异常
try{
//可能发生异常的代码
}catch(异常类型1 e){
//处理异常的代码
}catch(异常类型2 e){
//处理异常的代码
}catch(异常类型3 e){
//处理异常的代码
}…finally{
//需要最终执行的代码
}
try { login("admin","123456"); } catch (IllegalArgumentException e) { System.out.println("用户名不能为空"); }catch(RuntimeException e) { System.out.println("密码格式错误"); }catch(Exception e) { System.out.println("用户名错误"); }finally { System.out.println("滚......"); }
注意事项:
- 如果要对多种异常分开处理,子类异常必须放在前面
- 如果没有单独去捕获子类异常,使用父类异常一样也可以捕获
-
抛出异常
在方法的参数后面跟上 throws 异常类型
public static Date returnDate(String date) throws ParseException { SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return sf.parse(date); }
b. 两种异常处理区别
- 抛异常:发生异常程序会立即停止执行
- 捕获异常:try块里面的代码发生异常,会执行catch块里面的代码,程序不会停止运行
c. 自定义异常
实现步骤:
-
自定义一个类,继承已知的异常类
-
如果要携带错误信息,提供一个有参构造方法,功能体去调用父类的构造方法
public MyException(String s){ super(s); }
2. 线程
a. 创建线程的两种方法
方式一:
-
自定义类继承Thread类
-
覆写Thread类中的run()方法【run()方法中写具体的业务逻辑代码】
-
创建子类对象并且调用start()方法启动线程
public class ThreadTest { public static void main(String[] args) { GameThread g1 = new GameThread("g1"); GameThread g2 = new GameThread("g2"); g1.start(); g2.start(); } } class GameThread extends Thread{ String name; public GameThread() { } public GameThread(String name) { this.name = name; } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(name+"线程开启了"); } } }
方式二:
-
自定义类实现Runnable接口
-
重写run()方法
-
创建实现类对象
-
创建Thread对象并且把实现类对象作为实参传入,调用start()方法启动线程
public class ThreadTest2 { public static void main(String[] args) { MusicThread musicThread = new MusicThread(); new Thread(musicThread,"t1").start(); new Thread(musicThread,"t2").start(); } } class MusicThread implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+"线程启动了............."); } } }
b. 调用start()方法和run()方法的区别
调用start()会开启一个新的线程,当线程分配到CPU资源的时候,jvm会调用run()方法执行逻辑
调用run()相当于调用了一个普通的方法
18. 线程&数据结构
1. 处理线程的三种方法
a. 同步代码块
public class Ticket extends Thread{
static int num = 50;
String name;
public Ticket() {
super();
}
public Ticket(String name) {
this.name = name;
}
@Override
public void run() {
while(num>0) {
synchronized (Ticket.class) {
if(num>0) {
System.out.println(name+"的票号为:"+num);
num--;
}
}
}
}
}
b. 同步方法【不适用继承Thread类】
public class Ticket implements Runnable{
static int num = 50;
@Override
public void run() {
while(num>0) {
onSale();
}
}
private synchronized void onSale() {
if(num>0) {
System.out.println(Thread.currentThread().getName()+"的票号为:"+num);
num--;
}
}
}
c. lock
public class Ticket implements Runnable{
static int num = 50;
//初始化锁
private static Lock lock = new ReentrantLock();
@Override
public void run() {
while(num>0) {
//上锁
lock.lock();
//需要上锁的业务代码
try {
if(num>0) {
System.out.println(Thread.currentThread().getName()+"的票号为:"+num);
num--;
}
}finally {
//释放锁
lock.unlock();
}
}
}
}
2. 常见的数据结构
数组、链表、栈、堆、队列、图、树、散列表
栈:FILO【先入后出】
队列:FIFO【先入先出】
3. 模拟ArrayList
public class MyArrayList {
//创建一个长度为10的数组
private Object[] arr;
//定义无参构造方法初始化长度
public MyArrayList() {
this(10);
}
//定义带参构造方法传入指定长度
public MyArrayList(int length) {
arr = new Object[length];
}
//定义一个变量用于表示调用方法次数
private int size = 0;
//数据添加方法
public void add(Object obj) {
//添加数据长度超过数组长度进行扩容
if(size >= arr.length) {
//定义扩容规则
int le = (int) (size*1.5);
Object[] newArr = new Object[le+1];
System.arraycopy(arr, 0, newArr, 0, size);
arr = newArr;
}
//把数据添加到数组中,并且结束后为下一次调用方法做准备
arr[size++] = obj;
}
//返回指定索引的数据功能
public Object get(int index) {
if(index < 0 || index > size) {
throw new IndexOutOfBoundsException("非法参数,请参考0-"+(size-1));
}
return arr[index];
}
//返回存储了多少个数据的功能
public int getSize() {
return size;
}
//删除指定索引数据的功能
public void removeByIndex(int index) {
if(index < 0 || index > size) {
throw new IndexOutOfBoundsException("非法参数,请参考0-"+(size-1));
}
System.arraycopy(arr, index+1, arr, index, arr.length-index-1);
size--;
}
//返回指定数据第一次出现的索引
public int getFirstIndex(Object obj) {
if(obj == null){
for (int i = 0; i < arr.length; i++) {
if(arr[i] == obj) {
return i;
}
}
}else{
for (int i = 0; i < arr.length; i++) {
if(obj.equals(arr[i])) {
return i;
}
}
}
throw new RuntimeException("集合中不存在此元素");
}
//删除指定的数据
public void remove(Object obj) {
for (int i = 0; i < arr.length; i++) {
if(arr[i] == obj) {
removeByIndex(i);
}
}
}
//重写打印方法
@Override
public String toString() {
//定义一个新的数组,用于接收老数组中有效数据
Object[] newArr = new Object[size];
//对老数据中的有效数据进行复制
System.arraycopy(arr, 0, newArr, 0, size);
//打印有效数据
return Arrays.toString(newArr);
}
}
19. 集合List和Set
1. ArrayList
特点:
- 底层基于数组实现,连续内存
- 有序(添加顺序)、元素可重复、非线程安全的
- 随机访问速度快,基于下标索引访问
- 添加和修改效率较低
构造方法:
- 无参构造方法默认初始容量为10
扩容机制:
存储元素超出数组容量就会进行扩容,扩容为原容量的1.5倍
常用方法:
增删改查
遍历方法:
-
普通for循环
-
增强for循环
-
迭代器循环
Iterator iterator = list.iterator(); while(iterator.hasNext()) { System.out.println(iterator.next()); }
2. LinkedList
特点:
- 底层基于链表,无需连续内存
- 随机访问慢(沿着链表遍历)
- 插入和删除效率高
PS:其他和ArrayList差不多,参考ArrayList
3. HashSet
特点:
- 存储元素不可重复
- 存储元素是无序的
- 集合元素可以为null,但是只能有一个
扩容机制:
底层为HashMap,jdk1.8以后为数组+链表/红黑树,初始容量为16,加载因子为0.75,每次扩容为当前容量的2倍
常用API:
添加和删除,没有直接的修改和获取方法
遍历方式:
- 增强for循环
- 迭代器遍历
底层:
基于hash表实现数据存储(HashSet是基于HashMap实现的,而HashMap底层是基于hash表实现的)
1.8之后HashMap底层是基于数组+链表/红黑树存储的,初始容量16,加载因子0.75,树化阈值为8
树化条件:链表长度超过阈值8,数组容量≥64
判断两个对象是否重复:
需要hashcode和equals两者进行判断,结果都为true才会判定存储对象重复。存储自定义对象的时候,如果需要将对象添加进入set集合中的时候,就需要重写hashcode和equals方法
//AbstractSet抽象类中定义的equals
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection<?> c = (Collection<?>) o;
if (c.size() != size())
return false;
try {
return containsAll(c);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
}
//AbstractSet抽象类中定义的hashCode
public int hashCode() {
int h = 0;
Iterator<E> i = iterator();
while (i.hasNext()) {
E obj = i.next();
if (obj != null)
h += obj.hashCode();
}
return h;
}
4. TreeSet
特点:
- 存储元素不可重复
- 不保证添加的顺序,自然排序
- 集合的存储的值可以为null,但是只能有一个
构造器:
无参构造:默认根据其元素的自然排序进行排序
底层:
基于红黑树实现
排序(排序的时候集合中的元素是同一个类型):
-
自然排序:只要实现了Compareable接口就可以实现自然排序
- JDK的八大包装类和String可以直接进行排序,内部已经实现了Compareable接口
- 自定义类,实现Compareable接口,重写接口中的compareTo方法
-
定制排序:TreeSet(Comparator<? super E> comparator) :构造一个新的空 TreeSet,它根据指定比较器进行排序
- 实现类的方式,新建实现类对象,并且作为构造方法的参数传入
- 匿名内部类的方式
- lambda表达式
5. 正则表达式
定义:
- 正则表达式就是使用一系列预定义的特殊字符来描述一个字符串的格式规则,然后使用该规则匹配某个字符串是否符合要求;java中的正则也是字符串
特殊符号:
.:代表任意一个字符,如果想表示一个.,则需要\\.
\:转义字符
d:任意一个数字,相当于[0-9],注意\d的反斜杠\也需要转义,所以在代码中\d应该写成\\d
\w:表示任意一个数字,大小写字母,下划线
\s:表示任意一个空白[空格,TAB制表位,换行]
*:至少0次(前面紧挨着的字符,并不是前面的所有字符)
+:至少1次(前面紧挨着的字符,并不是前面的所有字符)
?:0次或1次(前面紧挨着的字符,并不是前面的所有字符)
[]:代表[]中的任意一个字符,如果只有一个字符,可以不写[]
{}:代表前面紧挨着的字符出现的次数
{n}:前面的字符出现n次
{n,}:前面的字符至少出现n次
{n,m}:前面的字符出现n到m次
():代表整体
|:代表或
^和$:代表正则的开始和结束,如果^出现在字符串的中间,代表非
20. Map
1. HashMap
- Map的继承体系
- |-- HashMap类【无序,非线程安全】 - 底层基于Hash表
|-- TreeMap类【自然排序,非线程安全】 - 底层基于红黑树
|-- HashTable类【无序,线程安全】 - 底层基于Hash表
|–Properties子类 - 特殊的Map【key和value都是字符串】 - 项目常用
- |-- HashMap类【无序,非线程安全】 - 底层基于Hash表
- HashMap的类图:
- 特点:
- 无序,非线程安全,底层基于hash表实现
- 构造器:无参构造HashMap() 构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap
- 常用API:
- V put(K key, V value) :在此映射中关联指定值与指定键
如果key值不存在:就是添加操作
如果key值存在:就是修改 - V get(Object key) :返回指定键所映射的值;如果对于该键来说。没有key,则返回 null。
- boolean containsKey(Object key) :如果此映射包含对于指定键的映射关系,则返回 true
- V remove(Object key) :从此映射中移除指定键的映射关系(如果存在)。
- int size() :返回此映射中的键-值映射关系数。
- V put(K key, V value) :在此映射中关联指定值与指定键
- 遍历的API:
遍历key:keySet()
遍历value:values()
遍历key-value:EntrySet()
2. Properties
- 继承体系
- Properties作用:
- 专门用来操作key和value都是字符串的情况
注意:使用Properties最好用自己的API,不要用继承的。例如添加put() - 将内存中的数保存在磁盘文件中【.txt】 - 持久化【将内存中的数据保存在可掉电设备中 - 硬盘,U盘(.txt,.xmind,数据库mysql)】
- 将文件中的数据读取到内存
- 专门用来操作key和value都是字符串的情况
3. Collections集合工具类
- 数组的工具类:Arrays
- List list = Arrays.asList(T… a): 将可变参数[数组]转成list集合
- Arrays.sort(数组对象):对数组进行排序 - 默认升序的
- 集合的工具类:Collections
- 获取集合中的最大值:max()
- 获取集合中的最小值:min()
- 对list集合进行排序:Collections.sort(List集合)
4. 泛型
-
在有泛型的时候你指定的是什么类型就是什么类型。如果忽略掉泛型就是Object,使用的时候需要强转
-
泛型的作用:
- 指定了泛型,规范了添加或修改的数据类型
- 不用在对Object进行强转了
注意:泛型不能用基本数据类 - 如果是基本数据类型的数据可以用包装类
-
常用的泛型类型:
KTVE - 特殊的含义
K - Key - 键
T - Type - 类型
V - Value - 值
E - Element - 元素
21. File
1. File类
Java 中认为文件夹和文件都属于文件
- 构造方法:
- File(String pathname):传入给定路径名字字符串,创建新的File对象
- File(File parent, String child):传一个父路径的File对象,和一个子路径字符串,创建一个新的File对象
- File(String parent, String child):传一个父路径字符串,和一个子路径字符串,创建一个新的File对象
- 常用API
- 判断相关API
- boolean exists():判断文件路径是否存在
- boolean isDirectory():判断是否是文件夹
- boolean isFile():判断是否是文件
- boolean isHidden():判断是否是隐藏文件
- 操作文件API
- boolean createNewFile():当文件不存在的时候创建一个新的文件,并返回是否创建成功
- boolean mkdir():当这个文件夹不存在的时候创建一个单层级文件夹,并返回是否创建成功
- boolean mkdirs():当文件夹不存在的时候创建一个多层级(包括一层)文件夹,并返回是否创建成功
- boolean delete():删除文件或者文件夹(只能删除最后一级)
- list相关API
- String[] list():返回一个字符串数组,表示当前目录下的文件或者文件夹名
- File[] listFiles():返回一个文件数组,表示当前目录下的文件或者文件夹file对象
- static File[] listRoots():返回一个文件数组,表示盘符
- 判断相关API
2. 文件过滤方法
-
自定义过滤方法
private static String[] fileNames(File file, String suffix) { // 判断是否是有效文件 if (file == null || !file.exists()) { return null; } // 新建一个容器 List<String> list = new ArrayList<>(); if (file.isFile()) { if (file.getName().endsWith(suffix)) { list.add(file.getName()); } } else { // 遍历找出每一个文件名,并对其进行判断 for (String name : file.list()) { if (name.endsWith(suffix)) { // 判断是否是文件 if (new File(file.getPath() + "\\" + name).isFile()) { list.add(name); } } } } // 集合转数组返回 return list.toArray(new String[list.size()]); }
-
实现 FilenameFilter 接口
实现 FilenameFilter 接口,重写 accept() 方法,并将实现类对象作为参数传入list()方法
String[] list = file.list(new FilenameFilter() { @Override public boolean accept(File dir, String name) { if(name.endsWith("txt")) { return true; } return false; } });
3. 递归
- 递归就是方法本身调用方法
- 递归一定要有出口,否则就会出现堆栈溢出的情况
案例:删除任意文件,包含多层级文件
private static void delete(File file) {
if(file == null || !file.exists()) {
return;
}
if(file.isFile()) {
file.delete();
}else {
File[] files = file.listFiles();
for (File fl : files) {
delete(fl);
}
file.delete();
}
}
22. IO流
1. 什么是IO流?
IO就是用于对数据的输入/输出(Input/Output),流的话是一种抽象概念,是对数据传输的总称,也就是说数据在设备之间的传输称为流,流的本质就是数据传输。IO流就是用来处理设备间数据传输问题的。
2. 流的分类
按照数据的流向可以将IO流分为输入流和输出流,输入流就是读数据,输出流就是写数据;还可以按照数据类型来区分,字节流和字符流。字节流的超类是InputStream和OutputStream,字符流的超类是Reader和Writer
3. 字节流
3.1 字节输入流
3.1.1 继承体系
|-- java.io.InputStream(抽象类)
|-- java.io.FileInputStream (常用子类)
3.1.2 构造方法
-
FileInputStream(File file):传入一个File对象创建一个FileInputStream
-
FileInputStream(String name):传入一个路径名来创建一个FileInputStream
3.1.3 常用API
- int read():每次读取一个字节,结果为ASCII码,数据无效返回-1
- int read(byte[] b):每次读取数组长度个字节,返回值为读取的字节数量,数据无效返回-1
3.1.4 循环读取
public class _03InputStreamTest {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream(new File("C:\\Users\\Administrator\\Desktop\\test\\aaa.txt"));
//read()循环读取
int i;
while((i = fis.read()) != -1) {
System.out.print((char)i);
}
fis.close();
}
}
public class _02InputStreamTest {
public static void main(String[] args) throws IOException {
FileInputStream fis
= new FileInputStream(new File("C:\\Users\\Administrator\\Desktop\\test\\aaa.txt"));
//read(byte[] b)
//准备参数数组
byte[] by = new byte[4];
//定义接收数
int len;
while((len = fis.read(by)) != -1) {
//String(byte[] bytes, int offset, int length) :通过使用平台的默认字符集解码指定的字节子阵列来构造新的 String 。
System.out.print(new String(by, 0, len));
}
fis.close();
}
}
3.2 字节输出流
3.2.1 继承体系
|-- java.io.OutputStream(抽象类)
|-- java.io.FileOutputStream (常用子类)
3.2.2 构造方法
- FileOutputStream(File file)
- FileOutputStream(File file, boolean append) : append表示追加还是覆盖
- FileOutputStream(String name)
- FileOutputStream(String name, boolean append)
3.2.3 常用API
- write(int b):写一个字节
- write(byte[] b) :写一个字节数组
3.3 字节流Copy
public class _02Copy {
public static void main(String[] args) throws IOException {
//准备输入输出源
FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\Desktop\\test\\bbb.txt");
FileOutputStream fos = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\test\\ccc.txt");
//单个字节读取写入
int i;
while((i = fis.read()) != -1) {
fos.write(i);
}
fos.close();
fis.close();
}
}
public class _03Copy {
public static void main(String[] args) throws IOException {
//准备输入输出源
FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\Desktop\\test\\bbb.txt");
FileOutputStream fos = new FileOutputStream("C:\\Users\\Administrator\\Desktop\\test\\ccc.txt");
//字节数组读取写入
byte[] by = new byte[4];
int len;
while((len = fis.read(by)) != -1) {
fos.write(by, 0, len);
}
fis.close();
fos.close();
}
}
3.4 字节流注意事项
- 不适合读取中文
- 可以读取任意文件,是万能流
4. 字符流
4.1 字符输入流
4.1.1 继承体系
| – java.io.Reader
| – java.io.InputStreamReader
| – java.io.FileReader
4.1.2 构造方法
-
FileReader(File file)
-
FileReader(String fileName)
4.1.3 常用API
- int read() :读一个字符
- int read(char[] cbuf):每次读取一个字符数组长度个字符
- int read(char[] cbuf, int offset, int length) :将字符读入数组的一部分。
4.1.4 循环读取
char[] ch = new char[3];
int len;
while((len = fr.read(ch)) != -1){
System.out.print(new String(ch, 0, len));
}
int i;
while((i = fr.read()) != -1) {
System.out.print((char)i);
}
4.2 字符输出流
4.2.1 继承体系
| – java.io.Writer
| – java.io.OutputStreamWriter
| – java.io.FileWriter
4.2.2 构造方法
- FileWriter(File file)
- FileWriter(File file, boolean append)
- FileWriter(String fileName)
- FileWriter(String fileName, boolean append)
4.2.3 常用API
参考字节输入流方法和API
4.3 字符Copy文件
public class _02Copy {
public static void main(String[] args) throws IOException {
//准备输入输出流
FileReader fr = new FileReader("E:\\workspace\\JavaSE\\day0518-io\\src\\cn\\itsource\\zzp\\_02OutputStream\\_01OutPutStream.java");
FileWriter fw = new FileWriter("E:\\workspace\\JavaSE\\day0518-io\\OutPutStream.java");
//循环读写
int i;
while((i = fr.read()) != -1) {
fw.write((char)i);
}
//关流
fw.close();
fr.close();
5. 字节流和字符流的区别
- 操作单位不一样,一个是字节,一个是字符
- 操作纯文本字符流效率会更高,但是不能操作非文本文件;所以在文件类型未知的情况下,使用字节流
6.转换流
概念:就是将字节流转变成字符流
1. InputStreamReader:字节输入流转字符输入流
构造方法:
- InputStreamReader(InputStream in)
- InputStreamReader(InputStream in, Charset cs)
2. OutputStreamWriter:字节输出流转字符输出流
- OutputStreamReader(OutputStream out)
- OutputStreamReader(OutputStream out, Charset cs)
7. 缓冲流
1. 作用
为了减少操作磁盘的次数,可以将大量读写数据暂存到缓冲区,等到数据比较多的时候再一次连接磁盘进行读写,从而达到高效读取的作用
2. 分类
- BufferedInputStream:字节缓冲输入流
- BufferedInputStream:字节缓冲输出流
- BufferedReader:字符缓冲输入流
- String readLine() :读取一行数据
- BufferedWriter:字符缓冲输出流
- void newLine():换行
8. IO流中的异常处理
1. 传统方法
使用传统try…catch…finally…
FileInputStream fis = null;//局部变量,在使用之前应该初始化
FileOutputStream fos = null;
try {
fis = new FileInputStream(src);
fos = new FileOutputStream(dest);
byte[] b = new byte[1024];
int len;
while((len = fis.read(b)) != -1){
fos.write(b,0,len);
}
} catch (Exception e) {
e.printStackTrace();//获取异常信息
}finally{
try {//先使用后关闭
if(fos != null)
fos.close();
if(fis != null )
fis.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
2. Java 7 提供的新方式
将需要关流的资源放在try()中,可能出现异常的代码放在{}中,这样可以实现自动关流操作
try(
FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dest);
){
byte[] b = new byte[1024];
int len;
while((len = fis.read(b)) != -1){
fos.write(b,0,len);
}
}catch(Exception e){
e.printStackTrace();
}
23. Lambda & Stream流
1. 函数式接口
1.1 概念:
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法(不包含继承自父类的抽象方法),默认方法和静态方法数量不作限制的接口
1.2 @FunctionalInterface
校验该接口是否是函数式接口,无论有或者没有都不会影响函数式接口本身,加上该注解,如果不是函数式接口就会报错
1.3 jdk8四大核心函数式接口
-
Consumer 【消费者接口】
@FunctionalInterface public interface Consumer<T> { void accept(T t); }
-
Supplier 【生产者接口】
@FunctionalInterface public interface Supplier<T> { T get(); }
-
Function 【供给者接口】
@FunctionalInterface public interface Function<T, R> { R apply(T t); }
-
Predicate 【断言型接口】
@FunctionalInterface public interface Predicate<T> { boolean test(T t); }
2. Lambda表达式
2.1 概述
可以理解为优化了匿名内部类的写法,只能用于函数式接口【箭头函数】
2.2 基本语法
函数式接口 变量名 = (参数...)->{
方法体
}
2.3 写法特点
- (参数…)表示参数列表;-> 表示连接符;{} 内部写执行方法体
- 如果没有参数,只需保留()
- 如果形参只有一个,可以省略数据类型和小括号
- 如果方法体只有一句,可以省略花括号,同时如果有return,省略花括号的时候必须省略return
- 形参的数据类型都可以省略
- Lambda内部使用了局部变量,局部变量必须是fina修饰的,如果没有被修饰,那么默认会加上,且变量不能被修改
2.4 代码演示
public class LambdaTest {
public static void main(String[] args) {
MyFunctionInterface my1 = (a)->{
return ++a;
};
System.out.println(my1.add(1));
System.out.println("<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>");
MyFunctionInterface my2 = a-> ++a;
System.out.println(my2.add(3));
}
}
3. 方法引用
3.1 概述
方法引用实质上是对特殊的lambda表达式的一种简写;当Lambda体中只是执行一个方法调用时,可以直接通过方法引用的形式可读性更高一些。方法引用是一种更简洁易懂的Lambda表达式
3.2 方法引用之构造方法
语法:接口名 变量名 = 类名::new
代码演示:
public class LambdaTest1 {
public static void main(String[] args) {
/*MyFunctionInterface my = (name , age) -> {
return new Student(name,age);
};*/
MyFunctionInterface my = Student::new;
System.out.println(my.insert("张三", 20));
}
}
3.3 方法引用之静态方法
语法:函数式接口 变量名 = 类名::静态方法名
代码演示:
/*
正常lambda
*/
MyFunctionInterface my = (a,b)->{
return Math.max(a, b);
};
/*
lambda 静态方法引用
*/
MyFunctionInterface my = Math::max;
3.4 方法引用之实例方法
语法:函数式接口 变量名 = 对象名::实例方法名
代码演示:
// MyFunctionInterface 函数式接口;MathTest 实例方法所在的类;getMax 自定义的实例方法
MyFunctionInterface my = new MathTest()::getMax;
4. Stream流
4.1 概念
Stream是一种用于操作集合或数组的元素序列,Stream流是对集合和数组中的数据进行操作【可以看做是管道流水线】
4.2 Stream流执行流程图
![img](https://i-blog.csdnimg.cn/blog_migrate/1da1a196e3934f70f777ea676b899d86.png)
4.3 Stream流常用方法
-
forEach :Stream 提供了新的方法 ‘forEach’ 来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8); list.stream().forEach(System.out::println);
-
map :map 方法用于映射每个元素到对应的结果。以下代码片段使用 map 输出了元素对应的平方数
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5); // 获取对应的平方数 numbers.stream().map( i -> i*i).forEach(System.out::println);
-
filter : filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤出空字符串
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); //过滤取出每一个不为空的元素 strings.stream().filter(string -> !string.isEmpty()).forEach(System.out::println);
-
limit : limit 方法用于获取指定数量的流。以下代码片段使用 limit 方法打印出 10 条数据
Random random = new Random(); //从随机流中获取十个随机数:并输出结果 random.ints().limit(10).forEach(System.out::println);
-
sorted : sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序
Random random = new Random(); //获取十个随机数,并排序 random.ints().limit(10).sorted().forEach(System.out::println);
4.4 流的分类
- 串行流【stream()】:串行流可以看做是一个工人操作流水线。执行效率较低,但是结果有序
- 并行流【parallelStream()】:并行流可以看做是多个人操作流水线。执行效率较高,但是无法保证顺序
4.5 Collectors
Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
//归约操作:操作元素之后返回新的集合
List<String> filtered = strings.stream().filter(string-> !string.isEmpty()).collect(Collectors.toList());
System.out.println("筛选列表: " + filtered);
//归约操作:操作元素之后元素之间用,号分割。合并了一个字符串
String mergedString = strings.stream().filter(string-> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);
//归约操作:统计获取空字符串的数量
long count = strings.stream().filter(string -> string.isEmpty()).count();
24. 单元测试&jar包&资源文件&设计模式
1. 单元测试(Junit4)
用于开发者自测,需要导入Junit4包。
-
语法:
@Test public void 方法名(){ //需要测试的代码! }
-
注意事项
- 只能被public修饰
- 不能有返回值
- 不能有形参
2. jar包
3. 资源文件(properties)
抽取资源文件,实现代码复用性,减少代码修改。
加载文件:
@Test
public void test1() throws Exception {
Properties prop = new Properties();
prop.load(Thread.currentThread().getContextClassLoader()
.getResourceAsStream("db.properties"));
System.out.println(prop.getProperty("username"));
}
4. 设计模式
4.1 单例模式【重点掌握】
概念:
每次获取对象都是同一个对象(非反射获取)
4.1.1 饿汉式
类加载同时就会被加载
代码:
public class Singleton1 {
private Singleton1() {
}
private static Singleton1 singleton = new Singleton1();
public static Singleton1 getIntance() {
return singleton;
}
}
4.1.2 懒汉式
使用时才会被加载
代码:
public class Singleton2 {
private static Singleton2 singleton2;
private Singleton2() {
}
public static Singleton2 getInstance() {
//双重检查锁
if(singleton2 == null) {
synchronized (Singleton2.class) {
if(singleton2 == null) {
singleton2 = new Singleton2();
}
}
}
return singleton2;
}
}
4.1.3 枚举单例
代码:
public enum Singleton5 {
//实例
INSTANCE;
}
4.2 装饰器模式
特征:不改变原有类文件和不使用继承的情况下,动态地扩展一个对象的功能
代码:
public class A {
public void show() {
System.out.println("我来啦!!!!!!");
}
public static void main(String[] args) {
A a = new A();
B b = new B(a);
b.show();
}
}
class B{
private A a;
public B(A a) {
this.a = a;
}
public void show() {
a.show();
System.out.println("我走啦!!!!!!");
}
}
4.3 简单工厂模式
目的:将对象的创建和使用分离,对象由工厂类创建,不自己创建。
代码:
public class PhoneFactory {
public static IPhone getPhone(String name) {
if(name.equals("apple")) {
return new ApplePhone();
}else if(name.equals("hua")){
return new HuaPhone();
}else {
return null;
}
}
}
4.4 适配器模式
目的:当前想要的对象没有,有的对象又不能使用,这个时候就可以将有但是不能用的对象适配一下,包装成能用的对象
代码:
public class LineImpl extends ChazuoImpl implements Line{
@Override
public void show() {
super.show();
System.out.println("导电~~~~~~~~~~");
}
}
25. JavaBean&反射&注解
1. JavaBean
1.1 概念:
官方概念:
JavaBean是Java开发一个可重用组件
通俗概念:
就是一个特殊或者满足一定规范的类
1.2 作用
- 方便传递数据【controller -> service -> dao】
- 方便重复使用
1.3 JavaBean规范
- 必须有包【包的层级关系】
- 必须有公有的无参构造【因为Spring框架底层创建对象是根据反射调用公有的无参构造】
- 必须有 getter 和 setter 方法
- 建议属性私有化
- 建议字段类型使用包装类
1.4 JavaBean实例
public class Student {
private Integer id;
private String name;
private Integer age;
public Student() {
super();
}
public Student(Integer id, String name, Integer age) {
super();
this.id = id;
this.name = name;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
}
}
1.5 Bean属性与对象属性
- 对象属性 = 实例属性【成员或字段】,没创建一个对象都有这个属性
- bean属性就是 getter 或 setter 方法上的属性
- 注意:bean属性和对象属性可以不一样,但是不建议,为了避免出问题
1.6 BeanUtils 的使用
BeanUtils 是第三方提供的工具类,所以使用之前需要导包
1.6.1 BeanUtils常用方法
-
setProperty();
-
getProperty();
//根据字段设置值setter,getter方法 private static void setUser() throws Exception { TestUser user=new TestUser(); //根据字段名称赋值 BeanUtils.setProperty(user,"name","xiaohei"); //根据字段名称取值 String name = BeanUtils.getProperty(user, "name"); System.out.println(name); //xiaohei System.out.println(user.toString());//TestUser{name='xiaohei', password='null', age=0} }
setProperty(); getProperty();方法只可以赋值对象中有的字段没有赋值不上,取值一样
-
populate()
//将map转化成对象 private static void setUserToMap() throws Exception { TestUser user=new TestUser(); Map<String,String> map= new HashMap<>(); map.put("name","xiaohua"); map.put("password","123445"); map.put("age","10"); map.put("haha","haha"); //将map key值与对象字段相对应的赋值, BeanUtils.populate(user,map); //只赋值name,password,age, haha自动抛弃 System.out.println(user.toString()); //TestUser{name='xiaohua', password='123445', age=10} }
populate() 也是只赋值对象中有的字段
-
copyProperties()
//赋值对象 private static void copy()throws Exception{ TestUser user=new TestUser(); TestUser user1=new TestUser(); user.setName("小黑"); user.setPassword("123456"); user.setAge(30); //copyProperties 第一个对象是要复制得到的对象,第二个是目标对象 BeanUtils.copyProperties(user1,user); System.out.println(user1.toString());//TestUser{name='小黑', password='123456', age=30} }
copyProperties()这个方法是全部赋值,比如user1之前有值,使用此方法之前的值会被user的值覆盖,全部赋值,也是copy相同字段的值;
copyProperties() 注意事项:
- 可以从map复制到对象,但是map中的key必须和bean中的对象相同;只能从map到bean对象,不能反之
- 复制对象可以是不同类型
- 相同类型复制
2. 反射
2.1 概述
反射就是在运行时动态获取类的信息【方法(method)、构造方法(constructor)、字段(field)、注解(annotation)等】的一种能力
2.2 Java文件编译和加载过程
2.3 作用
- 反射可以在运行时,动态的获取一个类的信息,可以创建对象,调用方法,修改属性等操作
- 反射是框架技术的基础
- 反射增加程序的灵活性,避免代码写死
- Java中的泛型、注解通过反射实现的
2.4 优缺点
- 优点:功能强大,灵活【可以获取类的任意信息,破坏代码】
- 缺点:效率低,破坏封装
2.5 反射常用API
-
获取字节码对象
- 类名.class
- 对象.getClass
- Class.forName(“完全限定名”)
-
获取构造方法
-
Constructor getConstructor(类<?>… parameterTypes):parameterTypes【构造方法中的形参数据类型】,返回对应形参的公共构造方法
-
Constructor<?>[] getConstructors():返回目标类中所有的公共构造方法
-
Constructor getDeclaredConstructor(类<?>… parameterTypes):返回对应形参的任意构造方法【包括私有】
-
Constructor<?>[] getDeclaredConstructors():返回目标类中所有的构造方法【包括私有】
-
T newInstance(Object… initargs):【Constructor类中方法,用于创建对线】使用此constructor对应的构造方法创建对象
-
void setAccessible(boolean flag):Constructor父类AccessibleObject中的方法,用于修改权限
@Test public void test2() throws Exception { Class<User> cls = User.class; Constructor<User> con = cls.getDeclaredConstructor(Integer.class); con.setAccessible(true); User user = con.newInstance(20); System.out.println(user); }
-
-
获取方法
-
Method getMethod(String name, 类<?>… parameterTypes):【name代表方法名,parameterTypes代表方法中的参数类型】,返回对应方法名和形参列表的公共方法
-
Method[] getMethods():返回目标类中所有的公共方法
-
Method getDeclaredMethod(String name, 类<?>… parameterTypes):返回对应方法名和形参列表的方法【包括私有方法】
-
Method[] getDeclaredMethods():返回目标类中所有方法【包括私有方法】
-
Object invoke(Object obj, Object… args):【Method类中的方法;参数一:对象,参数二:方法需要的实参,Object为方法的返回值】反射调用方法
@Test public void test6() throws Exception { // 获取Class Class<Student> cls = Student.class; // 获取对象 Student student = cls.newInstance(); // 获取该方法 Method eatMethod = cls.getMethod("eat", String.class); // 调用方法 该方法属于method类的。 // Object invoke(Object obj, Object... args) // 参数一:对象 参数二: 方法中的实参 // 返回值:obj,该方法的返回值。 该方法如果没有返回值,就返回null Object obj = eatMethod.invoke(student, "鸡腿"); System.out.println(obj); System.out.println("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); // 获取静态方法 Method method = cls.getMethod("study"); // 被static修饰的方法,可以不用传对象。 Object object = method.invoke(null); System.out.println("==================================="); // 获取私有的方法 Method declaredMethod = cls.getDeclaredMethod("show"); // 暴力破解 declaredMethod.setAccessible(true); // 调用私有的方法 Object invoke = declaredMethod.invoke(student); }
-
-
获取字段
与获取构造方法和获取方法类似
@Test public void test1() throws Exception { Class<User> u1 = User.class; //获取公有字段 Field[] fields = u1.getFields(); System.out.println(fields.length); for (Field f : fields) { System.out.println(f.getName()); } //获取私有字段 Field[] fields2 = u1.getDeclaredFields(); for (Field f : fields2) { System.out.println(f); } }
3. 注解
3.1 概念
注解【Annotation】是给程序看的,用来标记一些辅助信息,和类、接口、枚举等平级
3.2 JDK中四大内置注解
- @Override:验证是否重写父类的方法
- @Deprecated:标志当前方法或者类已经过时
- @SuppressWarnings:抑制警告
- @SafeVarargs:抑制堆污染
3.3 JDK元注解
3.3.1 概念
注解上面的注解,写在注解上面,对注解进行说明的
3.3.2 分类
- @Target:指定该注解的使用范围,没有指定什么地方都可以用【常用范围:TYPE[类]、METHOD[方法]、FIELD[字段]、CONSTRUCTOR[构造方法]】
- @Retention:指定自定义注解在代码中生效的生命周期【SOURCE[编译前]、CLASS[编译后]、RUNTIME[运行时]】
- @Documented:指定自定义注解在生成文档时,是否显示
- @Inherited:指定自定义注解是否被继承
3.3.3 自定义注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME) // 运行时也在
@Documented
public @interface VIP{
/**
* 0: 普通用户
* 1: 黄金vip
* 2. 钻石vip
* 3. 终身vip
* @return
*/
int value();
}