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中取出来的
//其他包装类也是一样
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 带参枚举定义
-
必须声明内部私有属性,方便获取枚举类型实例的参数值
-
必须声明跟实例对象相符合的内部私有构造方法
-
对外声明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;
}