Java-实用类

java对list集合进行分页

1、计算页数:

List<User> list = new ArrayList<>();
//add...
//pageNo表示当前页[1-n],pageSize表示每页大小
//方法一
int total = list.size();
int pageSum = (total -1) / pageSize +1;

//方法二
int total = list.size();
int pageSum = total%pageSize==0?total/pageSize:total/pageSize+1;

//方法三
int total = list.size();
int pageSum=(total+pageSize-1)/pageSize;

2、java8 使用stream api进行分页:

List<User> list = new ArrayList<>();
//add...

//split page
List<User> subList = list.stream().skip((pageNo-1)*pageSize).limit(pageSize).
collect(Collectors.toList());

3、普通方法分页:

//subList手动分页,page为第几页,rows为每页个数
public static List<T> subList(List<T> list, int page, int rows) throws Exception{
List<T> listSort = new ArrayList<>();
int size=list.size();
int pageStart=page==1?0:(page-1)*rows;//截取的开始位置
int pageEnd=size<page*rows?size:page*rows;//截取的结束位置
if(size>pageStart){
listSort =list.subList(pageStart, pageEnd);
}
//总页数
int totalPage=list.size()/rows;
return listSort;
}

----------------------------------------------------------------------------------------

1、String

1.1 String 实例化

String str1 = "xxx";

String string1 = "hello KH96";
System.out.println(string1); //hello KH96

String str1 = new String("xxx");

String string2 = new String("hello KH96");
System.out.println(string2); //hello KH96

String底层实现 private final char value[];

​ String底层是由私有final的数组实现的,对外没有提供修改的方法,字符串多次赋值,不是修改字符串的内容,而是改变字符串的引用地址

String源码

1.2 String常用方法

方法说明
length() 字符串的长度
equals() 比较的是字符串的内容
equalsIgnoreCase(String str) 忽略大小比较
toUpperCase() 转大写
toLowerCase() 转小写
concat(String str) 返回拼接后的字符串

length()

字符串的长度:length()方法,返回的是字符串的长度,即字符串的长度(不是字节数),区别去数组的length

String string1 = "hello KH96";
System.out.println(string1+"的长度:"+string1.length()); //hello KH96的长度:10

equals()

重写了Object类的equals方法,比较的是字符串的内容,不是对象

String string2 = "KH96";
String string3 = "KH97";
System.out.println(string2.equals(string3)); //false

equalsIgnoreCase(String str)

忽略大小比较

String string7 = "kh96";
String string8 = "KH96";
System.out.println("不忽略大小写比较:"+string7.equals(string8)); //false
System.out.println("忽略大小写比:"+string7.equalsIgnoreCase(string8)); //true

toUpperCase() 转大写 toLowerCase() 转小写

String string9 = "abCD";
System.out.println(string9.toUpperCase()); //ABCD
System.out.println(string9.toLowerCase());//abcd

concat(String str) 返回拼接后的字符串

"+"号也可以进行字符串拼接

concat(String str)

拼接字符串都创建了新的对象,在循环中尽量不要拼接字符串,会造成栈溢出;

String strig10 = "abc";
System.out.println(strig10.concat("bcd").concat("def"));//abcbcddef

1.3 String 字符查找/提取相关方法

方法说明
indexOf(String str) 返回str首次出现的下标
lastIndexOf(String str) 返回str最后一次出现的下标
substring(int index1) 截取下标index1,及以后的所有字符
substring(int index1,int index2) 截取下标index1到index2之间的字符串,包括index1,不包括index2
trim() 去除字符串的首尾空格
startsWith(String str) 是否以str开头
endsWith(String str) 是否以str结尾
contains(String str) 是否包含str
split(String str) 根据指定分割字符,将字符串拆分成字符串数组返回
toCharArray() 将字符串转为字符数组
replace(String str1,String str2) 用 str2 替换 str1
getBytes() 字符串转换为字节数组
getBytes("UTF-8") 字符串转换为字节数组,可指定编码
new String(byte[] bytes) 将字节数组转换为字符串

indexOf(String str)

返回str首次出现的下标,没有查到就返回-1

String string11 = "I am a good student in kh96";
System.out.println("good首次出现的位置:"+string11.indexOf("good")); //7

还可以通过ascii码值查询

String string11 = "I am a good student in kh96";
char char1 = 97;
System.out.println(char1); //a
System.out.println("参数支持int assic码值:"+string11.indexOf(97)); //2

lastIndexOf(String str)

返回str最后一次出现的下标,没有就返回-1

String string11 = "I am a good student in kh96";
System.out.println(string11);
System.out.println("t最后一次出现的下标:"+string11.lastIndexOf("t")); //18

substring(int index1)

截取下标index1,及以后的所有字符

index的范围[0,string.length()]

String string12 = "abcdefghijklmn";
System.out.println(string12.substring(5)); //fghijklmn

substring(int index1,int index2)

截取下标index1到index2之间的字符串,包括index1,不包括index2

index的范围[0,string.length()]

String string12 = "abcdefghijklmn";
System.out.println(string12.substring(5,8)); //fgh

小应用

String string14 = "KH90,KH91,KH92,KH93,KH94,KH95,";
System.out.println(string14.substring(0,string14.lastIndexOf(",")));//KH90,KH91,KH92,KH93,KH94,KH95

trim()

去除字符串的首尾空格

String string13 = "  KH  96  ";
System.out.println("原始长度"+string13.length()); //10
System.out.println("取出空格后长度"+string13.trim().length()); //6 "KH  96"

startsWith(String str) endsWith(String str)

startsWith(String str) 是否以str开头

endsWith(String str) 是否以str结尾

String string15 = "KH96.mp3";
System.out.println("是否是KH开头?"+ string15.startsWith("KH")); //true
System.out.println("是否是.mp3结尾?"+ string15.endsWith(".mp3")); //true

contains(String str)

判断字符串是否包含str

String string16 = "aaa bbb cc ddd";
System.out.println("是否包含bbb:"+ string16.contains("bbb")); //true
System.out.println("是否包含eee:"+ string16.contains("eee")); //false

split(String str)

根据指定分割字符,将字符串拆分成字符串数组返回

String string17_1 = "13501020304;15801020304;18901020304";
String[] phoneNumbers1 = string17_1.split(";"); //一种字符分割
System.out.println("手机号数组内容:"+ Arrays.toString(phoneNumbers2));

String string17_2 = "13501020304;15801020304!18901020304";
String[] phoneNumbers2 = string17_2.split(";|!"); //多种字符分割 用 | 隔开
System.out.println("手机号数组内容:"+ Arrays.toString(phoneNumbers2));
//[13501020304, 15801020304, 18901020304]

toCharArray()

将字符串转为字符数组

char[] chars1 = string18.toCharArray();
System.out.println(Arrays.toString(chars1)); //[K, H, 9, 8, 正, 在, 学, 习, 实, 用, 类]

replace(String str1,String str2)

用 str2 替换 str1

//获取一个16位的随机字符串
 String string19 = UUID.randomUUID().toString();
System.out.println(string19); //65c0844a-c437-4a65-89ca-84d4166325ff

//转换字符串,将-转换为*
System.out.println(string19.replace("-","*"));//65c0844a*c437*4a65*89ca*84d4166325ff
//去除字符串,将所有的-去除
System.out.println(string19.replace("-",""));//65c0844ac4374a6589ca84d4166325ff
//去16位随机数
System.out.println(string19.replace("-","").substring(0,16));//65c0844ac4374a65

getBytes() getBytes("UTF-8")

字符串转换为字节数组

String string20 = "abcd";
//getBytes() 没有指定编码
byte[] bytes = string20.getBytes(); 
try {
    //getBytes("UTF-8") 指定编码
    byte[] bytes2 = string20.getBytes("UTF-8");
    System.out.println(Arrays.toString(bytes2)); //[97, 98, 99, 100]
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}
System.out.println(Arrays.toString(bytes)); //Arrays.toString(bytes)

new String(byte[] bytes)

将字节数组转换为字符串

 byte[] bytes3 ={100,101,102}; //ascii码值
System.out.println(new String(bytes3)); //def
//配合上面getBytes进行转码
try {
    System.out.println(new String(bytes3,"utf-8")); //可以指定编码 def
} catch (UnsupportedEncodingException e) {
    e.printStackTrace();
}

char[] chars3 = {'K','H','9','6'};
System.out.println(new String(chars3));//KH96

2、StringBuffer

可变字符串类:StringBuffer
不同于String类:可以实现动态拼接字符串,而不会创建新的对象;
即:是一个可变字符串的对象,改变的是字符串对象中的内容;
不可以直接赋值,必须通过new创建对象;

2.1 StringBuffer实例化

new StringBuffer()

默认初始容量 16

StringBuffer sbf1 = new StringBuffer();
System.out.println("默认初始容量:"+sbf1.capacity());//16

初始容量底层实现

//StringBuffer()
public StringBuffer() {
        super(16); //初始容量16
    }

//AbstractStringBuilder(int capacity)
AbstractStringBuilder(int capacity) {
    value = new char[capacity];
}

new StringBuffer(int capacity)

指定初始容量

StringBuffer sbf2 = new StringBuffer(32);
System.out.println("只定始容量:"+sbf2.capacity()); //32

指定初始容量底层实现

public StringBuffer(int capacity) {
	super(capacity); //指定初始容量
}

StringBuffer(String str)

指定初始字符串,容量为字符串长度+16

StringBuffer sbf3 = new StringBuffer("Kh96");
System.out.println("指定初始字符串初始容量:"+sbf3.capacity()); //20

容量为字符串长度+16底层实现

public StringBuffer(String str) {
    super(str.length() + 16); //容量为字符串长度+16
    append(str);
}

2.2 StringBuffer常用方法

append(String str)

拼接字符串

StringBuffer sbf4 = new StringBuffer("userId=");
sbf4.append("U0001")
            .append(",userName=")
            .append("张三,age=")
            .append("18"); //userId=U0001,userName=张三,age=18

扩容机制

底层扩容,当拼接一个新的字符串,字符串数组长度不够,会进行动态扩容,
每次扩容都是前一个数组长度的2倍+2
最大扩容长度不能超过Integer的最大值 - 8;

void expandCapacity(int minimumCapacity) {
    int newCapacity = value.length * 2 + 2; //每次扩容都是前一个数组长度的2倍+2
    if (newCapacity - minimumCapacity < 0)
        newCapacity = minimumCapacity;
    if (newCapacity < 0) {
        if (minimumCapacity < 0) // overflow
            throw new OutOfMemoryError();
        newCapacity = Integer.MAX_VALUE;
    }
    value = Arrays.copyOf(value, newCapacity);
}

toString()

获取动态字符串内容

StringBuffer sbf4 = new StringBuffer("userId=");
sbf4.append("U0001")
            .append(",userName=")
            .append("张三,age=")
            .append("18");
String userInfo = sbf4.toString();
 System.out.println(userInfo); //userId=U0001,userName=张三,age=18

3、StringBuilder

用法和StringBuffer没有区别,唯一的区别就是StringBuffer是线程安全的,StringBuilder是非线程安全的
StringBuffer 的 append(String str)

StringBuffer源码

@Override
public synchronized StringBuffer append(String str) { //加了锁
    toStringCache = null;
    super.append(str);
    return this;
}

StringBuilder 的 append(String str)

StringBuilder源码

@Override
public StringBuilder append(String str) { //没有加锁
    super.append(str);
    return this;
}

----------------------------------------------------------------------------------------

1、Date

1.1 Date实例化

 Date date1 = new Date();
 System.out.println(date1); //Thu Jun 16 19:18:56 CST 2022

1.2 获取日期毫秒数

getTime()

System.out.println(date1.getTime()); //1655378336808
System.out.println(System.currentTimeMillis()); //通过系统获取时间1655378336867

1.3 日期格式化(从Date到String)

format(Date date)

SimpleDateFormat sdf1 = new SimpleDateFormat();
sdf1.applyPattern("yyyy-MM-dd HH:mm:ss"); //日期格式
System.out.println(sdf1.format(date1)); //2022-06-16 19:18:56

//简写
SimpleDateFormat sdf3 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf3.format(new Date()));

源码

public final String format(Date date)
{
    return format(date, new StringBuffer(),
                  DontCareFieldPosition.INSTANCE).toString();
}

字符串格式化的日期格式串的含义

//yyyy:四位的年 yy:两位的年
//MM 月份(0-11,代表1-12月)
//DD 年中的天数
//dd 月份中的天数(当前的天数,第几天)
//HH 24小时制(0-23)
//hh 12小时制(1-12)
//mm 分钟
//ss 秒数
//SSS 毫秒

1.4 日期解析(从String到Date)

parse(String source)

SimpleDateFormat sdf3 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String dateStr = "2021-06-16 11:45:45";
Date date2 = null;
try {
    //转换日期时,需要进行异常处理,处理ParseException,因为日期字符串可能不匹配
    date2 = sdf3.parse(dateStr);
    System.out.println(date2); //Wed Jun 16 11:45:45 CST 2021
} catch (ParseException e) {
    e.printStackTrace();
}

源码

public Date parse(String source) throws ParseException
{
    ParsePosition pos = new ParsePosition(0);
    Date result = parse(source, pos);
    if (pos.index == 0)
        throw new ParseException("Unparseable date: \"" + source + "\"" ,
                                 pos.errorIndex);
    return result;
}

1.5 日期比较

//日期比较,日期是对象,不可以直接使用比较运算符进行比较

方式1:直接比较两个日期对应的毫秒数,谁大,谁的日期就大(晚)

 String beginTime = new String("2020-08-09 13:32:20");
 String endTime = new String("2099-10-08 11:21:32");
 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 Date beginDate = df.parse(beginTime);
 Date endDate = df.parse(endTime);
 //因为时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。所以毫秒数越大时间越晚
 if(beginDate.getTime() < endDate.getTime()){ //beginDate is before endDate
 System.out.println("beginDate is before endDate");
 }else{
 System.out.println("beginDate is after endDate");
 }

方式2:借助Date的自带比较方法:before和after

if (beginDate.before(endDate)) {
    System.out.println("beginDate is before endDate");//beginDate is before endDate
}
if (endDate.after(beginDate)) {
    System.out.println("endDate is after beginDate");//endDate is after beginDate
}

1.6 时间增加

增加毫秒数

 //在当前基础上,增加1小时30分钟
 Date date3 = new Date();
 System.out.println("当前时间:"+sdf3.format(date3));//2022-06-16 19:50:20
 //增加1小时30分钟,需要获取当前毫秒数,在加上1小时30分钟毫秒数
 long time3 = date3.getTime();
 long addTime = (60+30)*60*1000;
 //根据毫秒数,转换为日期对象
 Date date4 = new Date(time3 + addTime);
 System.out.println("新时间:"+sdf3.format(date4));//2022-06-16 21:20:20

2、Calendar

2.1创建日历类对象

创建日历类对象,不是new出来的,而是通过日历类自带的静态方法getInstance,获取;

 Calendar calendar = Calendar.getInstance();

源码

public static Calendar getInstance()
{
    return createCalendar(TimeZone.getDefault(), 
                          Locale.getDefault(Locale.Category.FORMAT));
}

2.2获取相关信息

 //获取年份
int year = calendar.get(Calendar.YEAR);
System.out.println("当前年份:"+year);//2022

//获取月份,0-11代表1-12月,真实月份要+1
int month = calendar.get(Calendar.MONTH);
System.out.println("当前第几个月份:"+month);//5  代表6月份

//获取天数,是一年中哪一天
int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
System.out.println("当前天是一年中的第:"+dayOfYear); //167

2.3 通过日历类对日期进行操作

//通过日历类,获取当前日期对象
Date date = calendar.getTime();
System.out.println("当前日期:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date)); //2022-06-16 20:03:10

//在当前时间的基础上,灵活的添加时间,增加1小时30分钟
calendar.add(Calendar.HOUR,1);
calendar.add(Calendar.MINUTE,30);
System.out.println("新日期:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(calendar.getTime())); //2022-06-16 21:33:10

//通过日历类,创建时间对象
Calendar calendar2 = Calendar.getInstance();
calendar2.set(Calendar.YEAR,2021);
calendar2.set(Calendar.MONTH,6-1);
calendar2.set(Calendar.DAY_OF_MONTH,16);
System.out.println("新日期:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(calendar.getTime())); //2022-06-16 21:33:10
 

----------------------------------------------------------------------------------------

1、Math类

java.lang.Math类提供了常用的数学运算方法和两个静态常量E(自然对数的底数) 和PI(圆周率)
	// 绝对值
    System.out.println(Math.abs(-3.5)); // 3.5  
    // 最大值
    System.out.println(Math.max(2.5, 90.5));// 90.5
    // 随机数
    int random = (int) (Math.random() * 10); // 生成一个0-10之间的随机数
    // 四舍五入
	System.out.println(Math.round(3.45)); // 3
	System.out.println(Math.round(3.55)); // 4
	// 向上取整(取大于当前数的最小整数)
	System.out.println(Math.ceil(3.25)); // 4.0
	// 向下取整(取小于当前数的最大整数)
	System.out.println(Math.floor(3.25)); // 3.0
注:Math类方法很多,需要使用直接看API文档即可,不需要全部掌握

2、Random类 -- java.util.Random类

//简单介绍使用示例,不需要全部掌握,用到时候查下文档即可
// 创建一个Random对象
Random rand=new Random();
for(int i=0; i<20; i++){
    // 随机生成20个随机整数,并显示
    int num=rand.nextInt(10);// 返回下一个伪随机数,整型的
    System.out.println("第"+(i+1)+"个随机数是:"+num);
} 

运行结果

1个随机数是:4
第2个随机数是:8
第3个随机数是:5
第4个随机数是:3
第5个随机数是:1
第6个随机数是:1
第7个随机数是:1
第8个随机数是:8
第9个随机数是:8
第10个随机数是:7
第11个随机数是:5
第12个随机数是:7
第13个随机数是:9
第14个随机数是:4
第15个随机数是:0
第16个随机数是:5
第17个随机数是:0
第18个随机数是:3
第19个随机数是:8
第20个随机数是:9

3、生成指定范围的随机数

(int)(a + Math.random() * b )——>[a,a + b)

//(int)(a + Math.random() * b )——[a,a + b)
//4~9-->(int)(4 + Math.random() * (10-4)) )——[4,10)
for (int i = 0; i < 20; i++) {
    int RandomNum = 0;
    //生成0~9的随机整数
    RandomNum = (int)( 4+Math.random() * (10-4));
    System.out.println("生成的随机数为:"+ RandomNum);
}

运行结果

生成的随机数为:7
生成的随机数为:8
生成的随机数为:6
生成的随机数为:9
生成的随机数为:4
生成的随机数为:5
生成的随机数为:6
生成的随机数为:4
生成的随机数为:6
生成的随机数为:5
生成的随机数为:5
生成的随机数为:6
生成的随机数为:6
生成的随机数为:9
生成的随机数为:5
生成的随机数为:4
生成的随机数为:4
生成的随机数为:7
生成的随机数为:6
生成的随机数为:5

----------------------------------------------------------------------------------------

Java中的基本类型功能简单,不具备对象的特性,为了使基本类型具备对象的特性,所以出现了包装类,就可以像操作对象一样操作基本类型数据;包装类不是为了取代基本数据类型,而是在数据类型需要使用对象表示的时候,才使用包装类;

1.包装类的继承关系

2.基本数据类型和包装类的关系

基本数据类型包装类
byte Byte
char Character
short Short
int Integer
long Long
float Float
double Double
boolean Boolean

提示:所有的包装类+String类都是final修饰的,代表不可以被继承;

3.包装类对应的属性定义(以Integer为例)

System.out.println(Integer.SIZE); //位数 32位
System.out.println(Integer.BYTES); //字节数 4字节
System.out.println(Integer.MAX_VALUE); //最大值 2^32-1 2147483647
System.out.println(Integer.MIN_VALUE); //最小值 -2^32  -2147483648

4.包装类型的基本操作

基本类型,包装类型,String类型的相互转换

序号类型转换方法
1 int->Integer new Integer(primitive)
2 Integer->int Integer 对象.xxxValue()
3 Integer->String Integer 对象.toString()
4 String->Integer new Integer(String str)
5 int->String String.valueOf(primitive)
6 String->int Integer.paseXxx(String str)

4.1 int->Integer new Integer(primitive)

所有包装类都可以与之对应的基本数据类型作为参数,来构造他们的实例

//1基本数据类型 ->包装类
//1.1byte->Byte
byte b = 1;
Byte byte1 = new Byte(b);
//1.2 char->Character
Character character1 = new Character('a');
//1.3 short->Short
short s = 2;
Short short1 = new Short(s);
//1.4 int Integer
Integer integer1 = new Integer(4);
//1.5 long->Long
Long long1 = new Long(8);
//1.6 float->Float
Float float1 = new Float(4.0f);
//1.7 double->Double
Double double1 = new Double(8.0);
//1.8 boolean->Boolean
Boolean boolean1 = new Boolean(true);

注意:类型一定要对应;

Integer.valueOf(int a)

Integer integer5 = Integer.valueOf(200);
System.out.println(integer5); //200

源码

//也是new 的对象 跟new Integer(primitive)一样
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

4.2 Integer->int Integer 对象.xxxValue()

对象.xxxValue(),将包装类转换为对应的基本数据类型值,所有包装类都有

 Integer integer2 = new Integer(4);
 int int2 = integer2.intValue();
 System.out.println(int2); //4

4.3 Integer->String Integer 对象.toString()

对象.toString() 将基本数据烈性转换为String类型

Integer integer3 = new Integer(4);
String string3 = integer3.toString();
System.out.println(string3); //4

4.4 String->Integer new Integer(String str)

除Character类外,其他包装类可将一个字符作为参数构造他们的实例,前提:字符串内容必须跟对应包装类匹配

Integer integer4 = new Integer("4");
System.out.println(integer4);

Character类型 没有支持字符串的构造方法,因为字符串包含多个字符,Character只包含一个字符

Character a = new Character("aa"); //会报错

Integer.valueOf(String s) 也可以将String类型的数字转换为Integer类型

Integer Integer4_2 = Integer.valueOf("4"); 
System.out.println(integer4_2);//4

源码

public static Integer valueOf(String s) throws NumberFormatException {
    return Integer.valueOf(parseInt(s, 10)); 
    //先匹配数字,再调用new Integer
}

4.5 int->String String.valueOf(primitive)

基本类型转String类型

String string5 = String.valueOf(100);
System.out.println(string5); //100

String类的valueOf方法

4.6 String->int Integer.paseXxx

直接将字符串转换为对应的基本数据类型Character除外

int int6 = Integer.parseInt("123");
System.out.println(int6); //123

5.装箱,拆箱

5.1装箱

自动装箱,底层就是自动调用valuesOf方法实现

Integer integer5_1_1 = 400;
//实际上,系统执行了Integer obj = Integer.valueOf(400);
System.out.println(integer5_1_1); //400

5.2拆箱

Integer integer5_2_1 = new Integer(500);
System.out.println(integer5_2_1+integer5_2_1); //1000
//实际上执行了integer5_2_1.intValue()+integer5_2_1.intValue();   

6.包装类的缓存

6.1包装类的缓存范围

包装类缓存范围
Byte 全部缓存(-128—127)
Character <=127缓存
Short -128—127缓存
Integer -128—127缓存
Long -128—127缓存
Float 没有缓存
Double 没有缓存
Boolean 全部缓存(true,false)

6.2案例

Integer integer8_1 = 120;
Integer integer8_2 = 120;
System.out.println(integer8_1 == integer8_2); //true

Integer integer9_1 = 130;
Integer integer9_2 = 130;
System.out.println(integer9_1 == integer9_2); //false
//Integer类中有一个静态内部类IntegerCache,缓存了-128到127之间所有int数字的Integer对象
//120 在-128-127之间 ,产出的对象是从IntegerCache中取出来的
//其他包装类也是一样
JAVA 复制 全屏

6.3垃圾回收相关

Integer integer6_3_1 = 100;
integer6_3_1 = null; //integer6_3_1 不会被垃圾回收器回收
//这里虽然integer6_3_1被赋予null,但它之前指向的是cache中的Integer对象,而cache没有被赋null,所以Integer(100)这个对象还是存在

Integer integer6_3_2 = 128;
integer6_3_2 = null;//integer6_3_2 会被垃圾回收器回收
//integer6_3_2不是缓存中的对象,被赋予null后会被垃圾回收器回收

----------------------------------------------------------------------------------------

1、初识反射

1.1什么是反射

​ 反射是指在程序运行期间,能够观察和修改类或者类的对象的属性和行为的特性;

1.2 编译时与运行时

编译时

​ 编译时顾名思义就是正在编译的时候 . 那啥叫编译呢?就是编译器帮你把源代码翻译成机器能识别的代码 ;

​ 编译时就是简单的作一些翻译工作 ,比如检查老兄你有没有粗心写错啥关键字了啊.有啥词法分析,语法分析之类的过程. 就像个老师检查学生的作文中有没有错别字和病句一样;

运行时

所谓运行时就是代码跑起来了.被装载到内存中去了;

(你的代码保存在磁盘上没装入内存之前是个死家伙.只有跑到内存中才变成活的);

2、反射获取Class对象的四种方法

getClass()适合有对象实例的情况下
.class 仅适合在编译前就已经明确要操作的 Class
forName(“类的全类名”) 已明确类的全路径名
loadClass(“类的全类名”); 通过类加载器的loadClass(类的全路径名)

2.1 getClass()

 Person person = new Person();
//第一种:getClass() 需要有对象实例
Class<Person>  class1 = (Class<Person>)person.getClass();
System.out.println("==对象.getClass()==:"+class1); //com.kgc.reflection.Person

2.2 .class

//第二种:.class  需要明确操作的Class
Class<Person> class1_2 = Person.class;
System.out.println("==类.class==:"+class1_2);//com.kgc.reflection.Person

2.3 forName()

//第三种:forName()  需要类的全路径名
Class<Person> class1_3 = (Class<Person>)Class.forName("kgc.reflection.TestPerson");
System.out.println("==Class.forName(‘类路径’)==:"+class1_3);//com.kgc.reflection.Person

2.4 loadClass(“类的全类名”);

//4.通过类加载器的loadClass("类的全类名");
ClassLoader classLoader = this.getClass().getClassLoader();
Class<Person> class1_4 = (Class<Person>)classLoader.loadClass("com.kgc.reflection.Person");
System.out.println("通过类加载器的loadClass"+class1_4.getName());//com.kgc.reflection.Person

3、通过Class类初始化对象

3.1 无参构造方法

//先获得Class对象java
Class<Person> class2 = Person.class;
//创建实例对象,调用默认的空参构造
Person person2 = class2.newInstance();
System.out.println(person2); //Person{name='null', age=null}

3.2 有参构造方法

//先获得Class对象java
Class<Person> class2 = Person.class;
//通过Class对象获取有参构造类对象
Constructor<Person> constructor = class2.getConstructor(String.class, Integer.class);
//通过有参构造类对象的newInstance方法初始化对象
Person person3 = constructor.newInstance("化羽", 12);
System.out.println(person3); //Person{name='张三', age=30}

4、获取并修改属性值

4.1 对getDeclared...的理解

getField,Mothed,Constructor获取自己及父类的属性,方法,构造器(不包括私有的)
getDeclaredField,Mothed,Constructor 只获取自己类的属性,方法,构造器(包括私有的)

4.2 非私有属性

getField(String name)获取非私有属性
set(对象实例, Object value) 对指定实例的指定属性赋值
//name的定义:public String name;
//获取 非私有属性 name
Field fieldName = class2.getField("name");
//通过 属性实例名.set(对象实例,属性值)  对指定实例的指定属性赋值
fieldName.set(person3,"张三");
System.out.println(person3); //Person{name='张三', age=12}  //name发生了该改变

4.3 私有属性

getDeclaredField(String name)获取私有属性及其他属性
setAccessible(boolean flag) 是否取消 Java 语言访问检查(true是,false否)
set(对象实例, Object value) 对指定实例的指定属性赋值
//通过反射,获取运行时类的属性,private age,无法使用getField,必须是使用getDeclaredField,设置访问权限
//age的定义:private  Integer age;
//获得 私有属性age
Field fieldAge = class2.getDeclaredField("age");
//取消 Java 语言访问检查
fieldAge.setAccessible(true);
//通过 属性实例名.set(对象实例,属性值)  对指定实例的指定属性赋值
fieldAge.set(person3,30);
System.out.println(person3); //Person{name='张三', age=30}  //age发生了变化

5、获取并使用方法

5.1 无参方法

getMethod(方法名)获取无参方法
invoke(对象实例) 执行无参方法
//sayHi()方法:System.out.println("我是一个人,我的名字叫:"+name+",今年:"+age+"岁");
Method methodHi = class2.getMethod("sayHi");
methodHi.invoke(person3); //我是一个人,我的名字叫:张三,今年:30岁

5.2 有参方法

getMethod(方法名,参数类)获取有参方法
invoke(对象实例,参数) 执行有参方法
//sayHello(String nation)方法: System.out.println("我的国际是:"+nation);
Method methodHello = class2.getMethod("sayHello", String.class);
methodHello.invoke(person3,"中国"); //我的国际是:中国

5.3 私有方法

getDeclaredMethod(方法名,参数类)获取私有方法及其他方法
setAccessible(boolean flag) 是否取消 Java 语言访问检查(true是,false否)
invoke(实例,参数) 执行有参私有方法
//private void myMoney(double money){
//        System.out.println("我有"+money+"私房钱!");
//    }
//调用私有方法
//getDeclaredMethod("myMoney", double.class) 获取方法
Method myMoney = class2.getDeclaredMethod("myMoney", double.class);
//取消Java语言访问检查
myMoney.setAccessible(true);
//执行方法
myMoney.invoke(person3,2.5); //我有2.5私房钱!

6、类加载器

6.1 三种类加载器

BootStrap ClassLoader引导类加载器(Java的核心库,都是通过此加载器加载到内存的)
Extension ClassLoader 扩展类加载器
System ClassLoader 系统类加载器(所有的自定义加载列,都是系统类加载器)
//1.系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader); //sun.misc.Launcher$AppClassLoader@18b4aac2

//2.扩展类加载器:系统类加载器的父类
ClassLoader ExtClassLoader = systemClassLoader.getParent();
System.out.println(ExtClassLoader);//sun.misc.Launcher$ExtClassLoader@8efb846

//3引导类加载器扩展类加载器的引导类,无法直接获取;(Java的核心库,都是通过此加载器加载到内存的)
ClassLoader bootstapLoader = ExtClassLoader.getParent();
System.out.println(bootstapLoader); //null

//4.所有的自定义加载列,都是系统类加载器
ClassLoader classLoader4 = Person.class.getClassLoader();
System.out.println(classLoader4); //sun.misc.Launcher$AppClassLoader@18b4aac2

//5.Sting类的默认类加载器 ,引导类加载器(Java的核心库,都是通过此加载器加载到内存的)
ClassLoader classLoader5 = String.class.getClassLoader();
System.out.println(classLoader5); //null

几种类加载器的关系

在这里插入图片描述

双亲委派机制
Java虚拟机对class文件采用的是按需加载,加载类的class文件时使用的时双亲委派模式,即把请求交给父类处理,如果父类加载器还有父类,则进一步向上委托,直到启动类加载器,如果父类加载器加载成功,则返回,否则其子类加载器才会尝试加载。他是一种任务委派模式;

6.2 通过类加载器读取配置文件

jdbc.properties中的信息

#key=value
user_name=kh96
usre_pwd=123123

6.2.1 使用字节流将配置文件加载到内存中

//创建一个properties类对象
Properties properties = new Properties();

//创建一个字节输入流
//注意: 使用输入流来读取文件时默认在当前项目下查找
FileInputStream fileInputStream = new FileInputStream("src/jdbc.properties");

//调用properties的load()方法来读取加载到内存中的配置文件
properties.load(fileInputStream);

//获取配置文件中的信息
Object user_name = properties.get("user_name");
Object usre_pwd = properties.get("usre_pwd");

System.out.println("数据库的用户名:"+user_name); //kh96
System.out.println("数据库的密码:"+usre_pwd); //123123

6.2.2 使用ClassLoader(类加载器(具体是:系统类加载器))将配置文件加载到内存中来

//创建一个properties类对象
Properties properties = new Properties();

//通过当前类获取类加载器(系统类加载器)
ClassLoader classLoader = this.getClass().getClassLoader();
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

//通过系统类加载器对象调用getResourceAsStream()方法以流的形式获取资源,将配置文件加载到内存中
//注意: 我们使用类加载器的getResourceAsStream(String path)方法来获取资源时默认是在本项目的src文件目录之下获取
classLoader.getResourceAsStream("jdbc.properties");

//获取配置文件中的信息
Object user_name = properties.get("user_name");
Object usre_pwd = properties.get("usre_pwd");

System.out.println("数据库的用户名:"+user_name); //kh96
System.out.println("数据库的密码:"+usre_pwd); // 123123

----------------------------------------------------------------------------------------

1、 历史

​ 在 JDK 1.5 之前没有枚举类型,那时候一般用接口常量来替代(例如,public static final String male )。JKD1.5之后使用 Java 枚举类型 enum 可以更贴近地表示这种常量。

2、枚举的定义

2.1 简单枚举定义

GenderEnum 性别枚举

/*
    性别枚举
 */
public enum  GenderEnum {
    //定义枚举类型的变量(所有定义的变量都是当前枚举类型的实例名)
    //默认所有的定义变量,都是public static final 类型,不需要手动声明,而且一般都是大写
    MAN,WOMAN
}

测试

 public static void main(String[] args) {
        Student student = new Student();
        student.setStuNo("S001");
        student.setStuName("张三");
        //枚举类型赋值,都只能赋值为枚举中已经定义的实例名
        student.setGender(GenderEnum.MAN);
        student.setGender(GenderEnum.WOMAN);
        //非指定枚举类型,都不需要赋值,编译异常
        //student.setGender("男");
        student.setStuAge(20);

        System.out.println(student);

    }

运行结果:

2.2 带参枚举定义

  1. 必须声明内部私有属性,方便获取枚举类型实例的参数值

  2. 必须声明跟实例对象相符合的内部私有构造方法

  3. 对外声明get方法,只允许通过枚举的实例获取对应参数,不允许setter方法,枚举实例是通过构造方法初始化的

UserStatusEnum 用户状态枚举

/*
    用户状态
 */
public enum  UserStatusEnum {
    //带参数枚举类型
    STATUS_NORMAL("0","正常"),
    STATUS_LOCK("1","锁定"),
    STATUS_OFF("1","注销");

    //必须声明内部私有属性,方便获取枚举类型实例的参数值
    private String statusCode;
    private String statusMsg;


    //必须声明跟实例对象相符合的内部私有构造方法
    private UserStatusEnum(String statusCode,String statusMsg){
        this.statusCode = statusCode;
        this.statusMsg = statusMsg;
    }

    //对外声明get方法,只允许通过枚举的实例获取对应参数,不允许setter方法,枚举实例是通过构造方法初始化的
    public String getStatusCode() {
        return statusCode;
    }

    public String getStatusMsg() {
        return statusMsg;
    }

    //一般,在枚举类中,要提供一个静态的普通方法,当你不知道枚举的定义,
    //只知道状态码时,方便获取对应的说明
    public static String getMegsByCode(String statusCode){
        //枚举的遍历
        for (UserStatusEnum statusEnum : UserStatusEnum.values()){
            //判断传入的状态码,是否在内部定义中存在
            if(statusEnum.statusCode.equals(statusCode)){
                return statusEnum.statusMsg;
            }
        }
        //不存在
        return null;
    }
}

测试

public class TestUserStatusEnum {
    public static void main(String[] args) {
        //测试带参枚举
        //假设当前用户状态枚举是锁定
        System.out.println(UserStatusEnum.STATUS_NORMAL.getStatusCode()); //0
        System.out.println(UserStatusEnum.STATUS_NORMAL.getStatusMsg());  //正常
        //假设当前用户状态枚举是注销
        System.out.println(UserStatusEnum.STATUS_OFF.getStatusCode()); //1
        System.out.println(UserStatusEnum.STATUS_OFF.getStatusMsg());  //注销

        //测试枚举提供的普通方法
        //假设知道用户的状态是0,如何获取对应的说明
        System.out.println(UserStatusEnum.getMegsByCode("0")); //正常

    }
}

3、Switch选择结构支持的类型

byte
char
short
int
Enum(枚举,JDK1.5增加)
String (JDK1.7增加)

注意:一旦入参是一个枚举类型的实例,所有的case选择允许值都必须是枚举类型声明的实例常量;

 //验证switch选择结构支持的类型:int及以下(int short char byte) 枚举 String
switch(UserStatusEnum.STATUS_NORMAL){  //正常
        //一旦入参是一个枚举类型的实例,所有的选择允许值都必须是枚举类型声明的实例常量
    case STATUS_OFF:
        System.out.println("注销");
        break;
    case STATUS_LOCK:
        System.out.println("锁定");
        break;
    case STATUS_NORMAL:
        System.out.println("正常");
        break;
}
posted @ 2022-12-01 15:11  hanease  阅读(88)  评论(0编辑  收藏  举报