12.1 时间类型(重点)
Date类来自于java.util包中,用于显示当前的系统时间。
1.语法:
import java.util.Date;
Date 对象名=new Date();
创建了一个当前系统时间对象,如果要获具体的年,月,日,时,分秒的时候
工具提示中划删除线的方法代表是过时的方法,过时的方法不建议使用,非要使用也没问题。@Deprecated 只要方法上有这个注解代表当前的方法过时了,不建议使用。
@Override 重写
@Test 测试
@Deprecated 过时
2.date.getTime()获取date具体时间的时间戳
System.currentTimeMillis()当前时间的时间戳
3.按指定格式显示日期
import java.util.Date;
public class Main2 {
public static void main(String[] args) {
Date date = new Date();
SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String time = format.format(date);
System.out.println(time);
}
}
SimpleDateFormat 日期格式化类
SimpleDateFormat format=new SimpleDateFormat("指定格式");
yyyy-MM-dd hh:mm:ss
yyyy-MM-dd
hh:mm:ss
将日期转换为指定格式的字符串
String time = format.format(date);
将指定的日期格式的字符串转换为日期对象,同时这也是创建指定日期Date类型对象的方式
Date oldDate=format.parse("2022-6-23 00:00:00");
public class Main2 {
public static void main(String[] args) {
Date date = new Date();
SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String time = format.format(date);
System.out.println(time);
System.out.println(date.getTime());//获取的当前系统时间的时间戳,因为Date默认是当前时间
System.out.println("==================================");
try {
//将字符串转换为指定的日期
Date oldDate=format.parse("2022-6-23 00:00:00");
System.out.println(oldDate);
System.out.println(oldDate.getTime());//获取的是指定日期的时间戳
} catch (ParseException e) {
e.printStackTrace();
}
}
}
日期1.after( 日期2) 判断日期1是否在日期2之后,是返回true不是返回false
日期1.before( 日期2) 判断日期1是否在日期2之前,是返回true不是返回false
12.2 Calender
12.2.1 基本操作
在idea中打开一个类的结构列表alt+7
public class Main3 {
public static void main(String[] args) {
Calendar calendar=Calendar.getInstance();
System.out.println("年:"+calendar.get(Calendar.YEAR));
System.out.println("月:"+(calendar.get(Calendar.MONTH)+1));
System.out.println("日期:"+calendar.get(Calendar.DATE));
System.out.println("12小时:"+calendar.get(Calendar.HOUR));
System.out.println("24小时:"+calendar.get(Calendar.HOUR_OF_DAY));
System.out.println("分:"+calendar.get(Calendar.MINUTE));
System.out.println("秒:"+calendar.get(Calendar.SECOND));
System.out.println("当前月的周:"+calendar.get(Calendar.WEDNESDAY));
System.out.println("当前年的周:"+calendar.get(Calendar.WEEK_OF_YEAR));
System.out.println("星期:"+(calendar.get(Calendar.DAY_OF_WEEK)-1));
System.out.println("今年的第几天:"+calendar.get(Calendar.DAY_OF_YEAR));
}
}
Calendar.getInstance(); 一个静态方法用于创建Calendar对象
calendar.get(int fileds)获取指定部分的值 里面的参数是Calendar的静态成员变量
(calendar.get(Calendar.MONTH)+1月份要+1;
12.2.2 指定一个日期
calendar.set(2020,3,5);
calendar.set(2020,3,5,12,10,30); 设定指定的日期
public class Main3 {
public static void main(String[] args) {
Calendar calendar=Calendar.getInstance();
calendar.set(2020,3,5);//这里的3实际是4月
calendar.set(2020,3,5,12,10,30);
System.out.println("年:"+calendar.get(Calendar.YEAR));
System.out.println("月:"+(calendar.get(Calendar.MONTH)));
System.out.println("日期:"+calendar.get(Calendar.DATE));
System.out.println("12小时:"+calendar.get(Calendar.HOUR));
System.out.println("24小时:"+calendar.get(Calendar.HOUR_OF_DAY));
System.out.println("分:"+calendar.get(Calendar.MINUTE));
System.out.println("秒:"+calendar.get(Calendar.SECOND));
System.out.println("当前月的周:"+calendar.get(Calendar.WEDNESDAY));
System.out.println("当前年的周:"+calendar.get(Calendar.WEEK_OF_YEAR));
System.out.println("星期:"+(calendar.get(Calendar.DAY_OF_WEEK)-1));
System.out.println("今年的第几天:"+calendar.get(Calendar.DAY_OF_YEAR));
}
}
月份注意 外国的0代表一月,依次往后推。
12.2.3 获取时间戳
calendar.getTimeInMillis()
System.out.println(calendar.getTimeInMillis());
12.2.4 转换为Date对象
Date date=calendar.getTime();
12.3 System 类
System 系统类
System.currentTimeMillis() 当前系统时间戳
System.exit(0);退出程序的运行 0代表正常退出 非0代表非正常退出
System.out.println() 输出
System.in 系统标准的输入流
System.out 系统标准的输出流
System.gc(); 调用gc
System.getenv("CLASSPATH")调用环境变量的值
Properties properties = System.getProperties(); //Properties 读取一种.properites后缀后结尾文件的内容 k-v
System.out.println(properties.get("os.name")); //获取当前的操作系统
12.4 Runtime 类
运行时就是当前的这个java程序正在运行的时候
Runtime.getRuntime() 返回与当前Java应用程序关联的运行时对象。
Runtime.getRuntime().gc(); 调用gc回收机制
Runtime.getRuntime().freeMemory() 返回Java虚拟机中的可用内存量
Runtime.getRuntime().availableProcessors() 查看cpu的内核数量
12.5 Object类(重点)
Object类是java中所有类的父类,默认继承。任何类型的数据都可以向Object对象中存放。所有类都实现了Object类中的方法,包括数组。
equals() 作地址的判断
public boolean equals(Object obj) {
return (this == obj);
}
重写以后作地址和值的等值判断
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return stuNo == student.stuNo &&
stuAge == student.stuAge &&
stuName.equals(student.stuName);
}
toString()方法
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
//getClass().getName()获取的是当前这个类的完全限定名 包名.类名 例如 com.qf.entitys.Student
//Object中默认的toString()方法打印出来的结果:com.qf.entitys.Student@16f026b
public class Main5 {
public static void main(String[] args) {
Student zhangsan1=new Student(1,"张三",20);
Student zhangsan2=new Student(1,"张三",20);
System.out.println(zhangsan1.equals(zhangsan2));
//调用Object父类中的toString如果是直接打印可以不显示的调用toString方法 System.out.println(zhangsan1);
System.out.println(zhangsan1.toString());
}
}
在Student类中重写toString方法
@Override
public String toString() {
return String.format("学号:%d,姓名:%s,年龄:%d",this.stuNo,this.stuName,this.stuAge);
}
public class Main5 {
public static void main(String[] args) {
Student zhangsan1=new Student(1,"张三",20);
Student zhangsan2=new Student(1,"张三",20);
System.out.println(zhangsan1.equals(zhangsan2));
System.out.println(zhangsan1);
}
}
结果:
学号:1,姓名:张三,年龄:20
public final native Class<?> getClass(); 获取Class类型的对象 了解
了解
//让当前线程等待,阻塞,沉睡
public final void wait() throws InterruptedException {
wait(0);
}
//随机唤醒一条沉睡线程
public final native void notify();
//唤醒所有的沉睡线程
public final native void notifyAll();
12.6 包装类
八大基本数据每一个都有一个对应的包装类,为什么要使用包装类,因为有时程序是两端相互调用,如果有一个出错了,如果返回的值是一个基本数据类型的话,那调用的那一端无法根据结果来判断我的调用是否成功还是出了问题,因为一般出错了我们都要返回一个null,而基本数据类型无法反回null,所以现出了包装类型。
基本数据类型 | 包装类型 |
int | Integer |
long | Long |
byte | Byte |
short | Short |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
public class Main6 {
public static void main(String[] args) {
Integer num1=1;
Integer num2=0;
Integer result=calc(num1,num2); //包装类型与基本数据类型之间可以自动转换
if(result==null){ //现在可以根据结果来判定调用的方法是否是一个正常执行的状态。
System.out.println("程序出错");
}else{
System.out.println(result);
}
}
public static Integer calc(int num1,int num2){
try {
//程序正常执行的代码段
return num1/num2;
}catch (RuntimeException ex){//RuntimeException 进行时异常类
//是try的代码只要有一句出了错,立刻转入到catch中来执行,不会再中断程序的运行
return null;
}
}
}
在包装类型中如果要从一个类型转换到另一个类型时,不再像以前那样使用强制类型转换。每一个包装类中都提供了转换方法,方法的命名规则:数据类名名Value(); 例如 : byteValue() shortValue() doubleValue().....
public class Main7 {
public static void main(String[] args) {
Long data=100000l;
Integer n=data.intValue();
Double d=n.doubleValue();
Byte bt=d.byteValue();
Short st=bt.shortValue();
Float ft=st.floatValue();
}
}
12.7 BigDecimal
- 不变的,任意精度的带符号的十进制数字。
public class Main7 {
public static void main(String[] args) {
double n1=8.1;
double n2=3;
System.out.println(n1/n2);
}
}
运行结果:
2.6999999999999997
方法 | 案例 |
| 返回值为 |
| 返回 |
| 返回值为 |
subtract(BigDecimal subtrahend, MathContext mc) | 返回 |
| 返回值为 |
| 返回 |
| 返回值为 |
| scale保留小数位数,roundingMode舍入模式n1.divide(n2,2,BigDecimal.ROUND_HALF_UP) |
public class Main7 {
public static void main(String[] args) {
BigDecimal n1=new BigDecimal(8.1);
BigDecimal n2=new BigDecimal(3);
System.out.println(n1.divide(n2,2,BigDecimal.ROUND_HALF_UP));
}
}
import java.math.BigDecimal;
public class Main7 {
public static void main(String[] args) {
//这里用字符串进行构造,以下方法的结果没有浮动,如果用数字的话,以下的计算还是有浮动的。
BigDecimal n1=new BigDecimal("8.1");
BigDecimal n2=new BigDecimal("3");
System.out.println(n1.add(n2));
System.out.println(n1.subtract(n2));
System.out.println(n1.multiply(n2));
System.out.println(n1.divide(n2));
}
}
import java.math.BigDecimal;
import java.math.MathContext;
public class Main7 {
public static void main(String[] args) {
BigDecimal n1=new BigDecimal(8.1);
BigDecimal n2=new BigDecimal(3);
//MathContext具有指定的精度和 HALF_UP舍入模式。
/*
DECIMAL128 保留小数点后34位数字
DECIMAL32 保留小数点后7位数字
DECIMAL64 保留小数点后16位数
四舍五入模式
*/
System.out.println(n1.add(n2, MathContext.DECIMAL32));
System.out.println(n1.subtract(n2,MathContext.DECIMAL32));
System.out.println(n1.multiply(n2,MathContext.DECIMAL32));
//divide 保留2位小数,四舍五入模式
System.out.println(n1.divide(n2,2,BigDecimal.ROUND_HALF_UP));
}
}
12.8 国际化
i18n(其来源是英文单词 internationalization的首末字符i和n,18为中间的字符数)是“国际化”的简称。在资讯领域,国际化(i18n)指让产品(出版物,软件,硬件等)无需做大的改变就能够适应不同的语言和地区的需要。对程序来说,在不修改内部代码的情况下,能根据不同语言及地区显示相应的界面。
i18n-->地域识别码:
zh表示中文 CN 地域 中国
中国 汉语 zh CN
zh 中文 HK香港
中国香港 zh HK 中国澳门 zh MO 中国台湾 zh TW
en英语 GB英国
英国 英语 en GB
en英语 US美国
美国 英语 en US
Java程序要做国际化时,需要编写资源文件,资源文件的命名要用i18n的地域识别码。
1 、资源文件的创建
资源是创建一个以.properties为后缀的资源文件,这是一种k-v键值对文件,一个键对应一个值,编写形式:到时候在程序中通过左边的键可以获取右边的值
systemname=学习讲课系统1
user=请输入用户名1:
pwd=请输入密码1:
wel=欢迎你:%s,进入系统1
2、国际化资源文件的命名:
i18n.properties --> 默认操作系统语言环境的使用资源
如果用其他国家的,请以i18n为前缀后面跟地域识别码,例如:
美国英语:
i18n_en_US.properties
瑞士 法语 fr CH:
i18n_fr_CH.properties
你的程序要面向几个国家就要创建多少个资源文件,资源文件里的内容只是用于系统上文字信息的展示。
体验国际化:
public class Main9 {
public static void main(String[] args) {
ResourceBundle bundle=ResourceBundle.getBundle("i18n");
System.out.println(bundle.getString("user"));
System.out.println(bundle.getString("pwd"));
System.out.println(bundle.getString("systemname"));
}
}
问题,中文乱码
在settings-file encodings中的进行设置
ResourceBundle类
getBundle(String baseName)
使用指定的资源基本名称,默认语言环境(i18n_zh_CN.properties)或默认资源包(i18n.properties)。 如果这两个同时存在先找默认语言环境中的资源
getBundle(String baseName, Locale locale)
使用指定的资源基本名称,和地域的设置。Locale locale 对象表示具体的地理,政治或文化地区。
Locale中默认设置了一些地区可以进行选择
例如:
Locale localeUS=Locale.US; //美国
Locale localeCN=Locale.CHINA; //中国如果Local中没有的就需要自己创建
//Locale中没有默认设置的地区就创建locale对象
Locale localeHK=new Locale("zh","HK");
创建ResourceBundle对象
ResourceBundle bundle=ResourceBundle.getBundle("i18n");
这里采用的是与操作系统一样的语言环境来做资源,一般情况下使用i18n.properties这个文件,如果项目也配置了与操作系统一致的语言资源文件如,i18n_zh_CN.properties就选用一致的这个
ResourceBundle bundle=ResourceBundle.getBundle("i18n",localeUS);
这种创建方式就选择指定的语言区域的资源文件,它会将前经i18n加上区域和语言去资源文件中进行查找使用哪个
Locale localeUS=new Locale("en","US");
ResourceBundle bundle=ResourceBundle.getBundle("i18n",localeUS);
会这样来进行匹配 i18n_en_US.properties;
bundle.getString("资源文件中的键名") 这就来通键获取值
bundle.getString("systemname")
bundle.getString("user")
bundle.getString("pwd")
MessageFormat它是文本格式化工具类
MessageFormat.format("格式化的文本信息",可变参数);可以将可变参数去替换格式化信息中的固定格式的占位符
public class Main10 {
public static void main(String[] args) {
//MessageFormat.format的占位符可以使用多次
String format = MessageFormat.format("姓名:{0},年龄:{1},性别:{2},姓名:{0}", "张三", 20, '男');
System.out.println(format);
//%s这种占位符是一对一关系
System.out.printf("姓名:%s,年龄:%d,性别:%c,姓名:%s","张三",20,'男',"李四");
}
}
结果:
姓名:张三,年龄:20,性别:男,姓名:张三
姓名:张三,年龄:20,性别:男,姓名:李四
国际化整体案例
i18n.properties内容
systemname=学习读书系统
user=请输入用户名:
pwd=请输入密码:
wel=欢迎你:{0},进入系统{1},姓名:{0}
info=千锋教育重庆JavaEE-2202期开班典礼
error=用户名或密码错误
i18n_en_US.properties内容
systemname=Learning and lecture system
user=Please input a username:
pwd=Please input a password:
wel=Welcome to the system:{0},{1}
info=Qianfeng education Chongqing javaee-2202 class opening ceremony
error=username or password error
i18n_zh_CN.properties内容
systemname=学习读书系统1
user=请输入用户名1:
pwd=请输入密码1:
wel=欢迎你:{0},进入系统{1},姓名:{0}
info=千锋教育重庆JavaEE-2202期开班典礼1
error=用户名或密码错误1
i18n_zh_HK.properties内容
systemname=學習講課系統
user=請輸入用戶名:
pwd=請輸入密碼:
wel=歡迎你:{0},進入系統,{1}
info=千鋒教育重慶JavaEE-2202期開班典禮
error=用戶名或密碼錯誤
import java.text.MessageFormat;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.Scanner;
public class Main9 {
public static void main(String[] args) {
//Locale中没有默认设置的地区就创建locale对象
Locale localeHK=new Locale("zh","HK");
//直接从Locale默认设置的地区中获取
Locale localeUS=Locale.US;
Locale localeCN=Locale.CHINA;
ResourceBundle bundle=ResourceBundle.getBundle("i18n",loacledHK);
Scanner input = new Scanner(System.in);
System.out.println(bundle.getString("systemname"));
System.out.println(bundle.getString("user"));
String user=input.next();
System.out.println(bundle.getString("pwd"));
String pwd=input.next();
if("admin".equals(user)&&"123456".equals(pwd)){
System.out.println(bundle.getString("info"));
String wel = MessageFormat.format(bundle.getString("wel"), user, pwd);
System.out.println(wel);
}else {
System.out.println(bundle.getString("error"));
}
}
}
12.9 对象克隆
克隆对象,把一个对象复制给另一个对象,引用地址不一样,里面的属性值一样,用于一个项目中有大批量相同属性值的对象生产的时候使用。这样可以提高执行效率。因为new 类名()会在jvm中去做很多的事情,比如,计算类的大小,分配内存空间,寻找堆等操作....使用克隆就不会再去做这些事情。
public class Student implements Cloneable {
public Student(int stuNo, String stuName, int stuAge) {
this.stuNo = stuNo;
this.stuName = stuName;
this.stuAge = stuAge;
}
public Student() {
}
private int stuNo;
private String stuName;
private int stuAge;
@Override
public String toString() {
return String.format("学号:%d,姓名:%s,年龄:%d",this.stuNo,this.stuName,this.stuAge);
}
// clone()来自于Object类中,如果子类直接重写,那么无法实现克隆效果,那是因为没有实现Cloneable这个接口
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Main11 {
public static void main(String[] args) throws CloneNotSupportedException {
//对象就好比数组一个
int []num1={1,11,22}; //定义一个数组
int[] num2=num1; //这样赋值直接将地址给到另一个数组名,它们是一样的
int []num3= Arrays.copyOf(num1,num1.length); //复制数组中的元素到另一个数组中 ,这个数组和原数组是不一样的地址
System.out.println(num1);//num1
System.out.println(num2);//num2 这两个是一样的地址
System.out.println(num3); //一种完全复制的状态,地址不一样
System.out.println("===========================================");
Student student1=new Student(1,"张三",20);//定义一个对象
Student student2=student1; //把student1地址给student2
System.out.println("student1:"+student1);
System.out.println("student2:"+student2);
//先去堆中开辟空间
//计算你这个类有多大
//寻址,寻找这个类在堆中的地址
//才把地址给到对象中
//问题:如果现在这个项目中大批量的使用相同属性值的对象时怎么办。降低你的效率.
Student student3=(Student) student1.clone(); //类似于Arrays.copyOf(); 这两个的原理不一样啊
System.out.println("student3:"+student3);
}
}
Cloneable 里面没有任何的代码,没有属性没有方法定义,这种接口称为标记型接口,类似于一个记号,有了这个记号就代表你这个类可以去做某些事情了。Cloneable 代表你的类可以进行克隆了。
比如,Serializable标记型接口,有了它代表这个类可以进行序列化操作。(序列化,代表可以将这个类的对象以二进制的形式写入到文件中)
12.10 对象的比较(重点)
1、Comparable 接口,用于两个对象之间的比较,具体比较什么属性由开发者自己决定。compareTo在这个方法中来实现具体的代码逻辑。
public class Student implements Cloneable,Comparable{
@Override
public int compareTo(Object o) {
}
}
实现了这个接口以后Arrays.sort()才能进行排序。对象1.compareTo(对象2)比较大小。
public class Student implements Cloneable,Comparable {
public Student(int stuNo, String stuName, int stuAge) {
this.stuNo = stuNo;
this.stuName = stuName;
this.stuAge = stuAge;
}
public Student() {
}
private int stuNo;
private String stuName;
private int stuAge;
public int getStuNo() {
return stuNo;
}
public void setStuNo(int stuNo) {
this.stuNo = stuNo;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public int getStuAge() {
return stuAge;
}
public void setStuAge(int stuAge) {
this.stuAge = stuAge;
}
@Override
public String toString() {
return String.format("学号:%d,姓名:%s,年龄:%d",this.stuNo,this.stuName,this.stuAge);
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
/**
* 用于判断两个对象谁大谁小
* @param o 用于比较的对象和本身调用这个方法的对象进行比较
* @return 如果调用这个方法的对象比参数里的对象大返回正数,相等返回0,小于返回负数
*/
@Override
public int compareTo(Object o) {
//判断参数不能为空,参数必须是Student类型,否则报错
if(o==null||!(o instanceof Student)){
throw new RuntimeException("请传入一个Student类型的对象");
}
Student stu1=(Student)o;
//这里是按学号进行排序,如果第一个对象的学号大于stu1的学号计算出来肯定是一个正数
//如果相等肯定是0
//小于结果肯定是负数
//就满足了大于返回正数,相等返回0,小于返回负数的要求
return this.stuNo-stu1.getStuNo();
}
}
import java.util.Arrays;
public class Main12 {
public static void main(String[] args) {
Student[] stus={
new Student(23,"张三",20),
new Student(11,"李四",17),
new Student(106,"王五",88),
new Student(78,"赵六",56),
new Student(52,"朱七",42)
};
Arrays.sort(stus);
//System.out.println(Arrays.toString(stus));
Arrays.stream(stus).forEach(System.out::println);
}
}
运行结果:
学号:11,姓名:李四,年龄:17
学号:23,姓名:张三,年龄:20
学号:52,姓名:朱七,年龄:42
学号:78,姓名:赵六,年龄:56
学号:106,姓名:王五,年龄:88
2、Comparator<T> 对象比较器
面向对象有六大原则:
1、单一职责
2、开闭原则 扩展开放,修改关闭
3、里氏替换原则 装箱拆箱 is a 某东西属于哪种类型
4、依赖倒转原则 这个原则是开闭原则的基础,具体内容:针对接口编程,依赖于抽象而不依赖于具体。
5、接口隔离原则 使用多个隔离的接口,比使用单个接口要好
6、迪米特法则,又称最少知道原则 最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
7、合成复用原则 尽量使用合成/聚合的方式,而不是使用继承
@FuncationInterface 表示的是就当前的接口只能是一个函数式接口
public interface Comparator<T>{}
@FuncationInterface 函数式接口 接口中只能有一个方法的定义(抽象方法)叫函数式接口
int compare(T o1, T o2); 将两个对象传入进行比较,这里的判断逻辑与上面的comparaTo的逻辑一样
在不修改源码的情况,通过新增比较器类来实现对象的比较。
写好的原码不再修改
public class Teacher {
public Teacher() {
}
public Teacher(int teaNo, String teaName, int teaAge) {
this.teaNo = teaNo;
this.teaName = teaName;
this.teaAge = teaAge;
}
private int teaNo;
private String teaName;
private int teaAge;
public int getTeaNo() {
return teaNo;
}
public void setTeaNo(int teaNo) {
this.teaNo = teaNo;
}
public String getTeaName() {
return teaName;
}
public void setTeaName(String teaName) {
this.teaName = teaName;
}
public int getTeaAge() {
return teaAge;
}
public void setTeaAge(int teaAge) {
this.teaAge = teaAge;
}
@Override
public String toString() {
return "Teacher{" +
"teaNo=" + teaNo +
", teaName='" + teaName + '\'' +
", teaAge=" + teaAge +
'}';
}
}
新增比较器
package com.qf.entitys;
import java.util.Comparator;
public class TeacherComparator implements Comparator<Teacher> {
//还是和上面的那个比较的原理一样
@Override
public int compare(Teacher o1, Teacher o2) {
return o1.getTeaAge()-o2.getTeaAge();
}
}
import com.qf.entitys.Teacher;
import com.qf.entitys.TeacherComparator;
import java.util.Arrays;
public class Main14 {
public static void main(String[] args) {
Teacher[] teas={
new Teacher(23,"张三",56),
new Teacher(11,"李四",88),
new Teacher(106,"王五",17),
new Teacher(78,"赵六",20),
new Teacher(52,"朱七",42)
};
//创建比较器
TeacherComparator teacherComparator=new TeacherComparator();
//将比较器一起传入到排序方法中
Arrays.sort(teas,teacherComparator);
Arrays.stream(teas).forEach(System.out::println);
}
}
12.11 assert 断言
assert 我觉得程序执行到这里我去判断一个两个关键性变量的关系是否是我预估的关系。满足条件程序继续执行,要是不满足条件,程序中断
public class Main18 {
public static void main(String[] args) {
System.out.println("程序开始");
int a=10;
int b=9;
assert a>b:"stop"; //如果条件为真继续执行程序,如果条件为假程序执行中断,并打印后面调用的信息"stop"
System.out.println("第一次断言结束,满足条件");
a++;
b--;
assert a<b:"stop";
System.out.println("第二次断言结束,满足条件");
System.out.println(a);
System.out.println(b);
}
}
当前程序直接执行没有效果,需要开启断言模式再执行.
重新执行。
12.12 内部类(重点)
内部类就是在类中再去定义一个类。
1、在一个类(外部类)中再去定义一个类。
public class Teacher {
public Teacher() {
}
public Teacher(int teaNo, String teaName, int teaAge) {
this.teaNo = teaNo;
this.teaName = teaName;
this.teaAge = teaAge;
}
private int teaNo;
private String teaName;
private int teaAge;
public int getTeaNo() {
return teaNo;
}
public void setTeaNo(int teaNo) {
this.teaNo = teaNo;
}
public String getTeaName() {
return teaName;
}
public void setTeaName(String teaName) {
this.teaName = teaName;
}
public int getTeaAge() {
return teaAge;
}
public void setTeaAge(int teaAge) {
this.teaAge = teaAge;
}
@Override
public String toString() {
return "Teacher{" +
"teaNo=" + teaNo +
", teaName='" + teaName + '\'' +
", teaAge=" + teaAge +
'}';
}
//中文老师 内部类的创建语法。
public class ZHTeacher {
private final String subject="中文";
private String gender;
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public void show(){
//内部类对象的创建语法,依托于外部类对象来进行new创建,外部类对象设置了值之后,在内部的方法也能够访问外部对象设置的值。
System.out.println("工号:"+teaNo+"姓名:"+teaName+",年龄:"+teaAge+",专业:"+subject+",性别:"+gender);
}
}
}
public class Main15 {
public static void main(String[] args) {
Teacher teacher=new Teacher(1,"张三",20);
//内部类对象的创建语法,依托于外部类对象来进行new创建,外部类对象设置了值之后,在内部的方法也能够访问外部对象设置的值。
Teacher.ZHTeacher zhTeacher=teacher.new ZHTeacher();
zhTeacher.setGender("男");
zhTeacher.show();
}
}
1、内部类的定义与外部类是一致的,该继承的还是可以继承,该实现接口依然可以实现接口
2、内部类可以访问外部类所有的内容,属性,方法,包含私有的内容,这样的好处解决Java类只能单继承的缺陷。
3、外部类和内部类对于JVM来讲属于两个独立的类,编译会产生两个class。
4、内部类对象的创建要依托对外部类的对象,例如: Teacher.ZHTeacher zhTeacher=teacher.new ZHTeacher();
2 、在一个类中再去定义静态的类(静态内部类)。
public class Teacher {
public Teacher() {
}
public Teacher(int teaNo, String teaName, int teaAge) {
this.teaNo = teaNo;
this.teaName = teaName;
this.teaAge = teaAge;
}
private int teaNo;
private String teaName;
private int teaAge;
public int getTeaNo() {
return teaNo;
}
public void setTeaNo(int teaNo) {
this.teaNo = teaNo;
}
public String getTeaName() {
return teaName;
}
public void setTeaName(String teaName) {
this.teaName = teaName;
}
public int getTeaAge() {
return teaAge;
}
public void setTeaAge(int teaAge) {
this.teaAge = teaAge;
}
@Override
public String toString() {
return "Teacher{" +
"teaNo=" + teaNo +
", teaName='" + teaName + '\'' +
", teaAge=" + teaAge +
'}';
}
//这是外部类的静态属性
public static String height;
//中文老师 定义的是一个静态内部类
public static class ZHTeacher {
public static String subject="中文";
public static String gender;
public static void show(){
System.out.println("身高:"+height+",专业:"+subject+",性别:"+gender);
}
}
}
//调用代码:使用
//外部类名.内部类名.静态属性和方法
Teacher.height="175cm";
Teacher.ZHTeacher.subject="英语";
Teacher.ZHTeacher.gender="女";
Teacher.ZHTeacher.show();
问题:
Teacher teacher=new Teacher(1,"张三",30);
Teacher.setHeight(175.0);
Teacher.ZHTeacher zhteacher=new Teacher.ZHTeacher(); //创建静态内部类的对象
zhteacher.setGender("男");
System.out.println(Teacher.ZHTeacher.subject); //这是调用内部类的静态属性
System.out.println(teacher.toString());
zhteacher.show(); //调用内部类的方法
- 2.1 如果内部类是静态的,不能访问外部类的成员属性和成员方法
- 2.2 静态内部类只访问外部类的静态变量或静态方法
- 2.3 如果一个内部类中有一个静态属性或静态方法,整体类都要变成静态类。
- 2.4 静态内部类的创建对象 外部类.内部类 对象=new 外部类.内部类(); 创建静态类的对象是为了去调用静态类中的成员变量或方法
3、在方法中去定义一个类,这个类的作用域只在方法中。
public class Calc {
public void calc(){
Scanner input = new Scanner(System.in);
System.out.println("请输入第一个数:");
int n1=input.nextInt();
System.out.println("请输入第二个数");
int n2=input.nextInt();
class Add{
public Add(int num1, int num2) {
this.num1 = num1;
this.num2 = num2;
}
private int num1;
private int num2;
//add 对num1和num2内部成员变量进行正常的赋值
public int add(){
num1++; //改变它们的值是没有问题的;
num2++;
return num1+num2;
}
//n1和n2是方法中的局部变量
public int add1(){
n1++;//这里不让改变它们的值,因为默认会加上final关键词,常量
n2++;
return n1+n2;
}
}
Add a=new Add(n1,n2);
System.out.println(a.add1());
}
}
调用代码
public class Main16 {
public static void main(String[] args) {
Calc c=new Calc();
c.calc();
}
}
- 3.1 方法中的内部类这样写没有意义,了解即可
- 3.2 重点:类中的方法如果要改变局部变量,不会让改变的,因为在内部类的方法中的局部变量默认加上final关键字,为什么加final.
因为方法区中的方法的加载有一个压栈的过程,压进去是执行方法,方法执行完之后会出栈,出栈以后所有局部全部消失掉,访问都访问不了。这种情况下有可能在多线程中c.calc();的调用会早于a.add1()的调用先完成,在add1中访问n1和n2都有问题,如果加上final以后将n1和n2的值加载到常量池中,无论calc()有没有消失对于add1方法的访问不影响。
4、匿名内部类,没有类名,一般用于实例化抽象类或接口。重点
import com.qf.entitys.Calc;
public class Main17 {
public static void main(String[] args) {
Calc add=(num1,num2)->num1+num2; //这样只能对函数式接口这样写
Calc sub=new Calc() {
@Override
public Integer calc(int num1, int num2) {
return num1-num2;
}
};
System.out.println(add.calc(3, 5)); //程序执行到这里的调用时才会进入到匿名内部类中去执行具体的代码
System.out.println(sub.calc(10, 3));
}
}
接口:
package com.qf.entitys;
public interface Calc {
public Integer calc(int num1,int num2);
}
- 1、匿名内部类,是将类的定义直接赋值给一个对象,这样写仅限于代码量不多的类或接口
- 2、匿名内部类中无法修改外局的局部变量只能访问
- 3、匿名内部类中的方法不是在定义的时候执行,和常规的对象调用方法一样是在这个赋值对象调用方法的时候才执行
- 4、匿名内部类是重点,是以后lambda表达式的基础。
12.13 StringBuffer,StringBuilder(重点)
StringBuffer比String性能要高,节约内存空间,StringBuffer称为可变字符串。StringBuffer对于字符串的改变不会产生新的空间,都是在一个空间中完成的。StringBuffer还有一个兄弟StringBuilder他们两所有的操作API(方法)都一模一样。唯一的区别是StringBuffer线程安全(在多线程中能解决并发问题,多个人线程去争抢同一资源),StringBuilder非线程安全。synchronized本地锁多线程中的锁。
构造方法:
构造方法 | 案例 |
| StringBuffer stringBuffer=new StringBuffer(); 这样创建默认长度16 |
| StringBuffer stringBuffer=new StringBuffer('h');给定初始值为hStringBuffer stringBuffer=new StringBuffer("hello"); 给定初始值为hello |
| StringBuffer stringBuffer=new StringBuffer(5); 给定初始值长度为5 |
private transient char[] toStringCache;
transient关键字,它只能修饰变量,不能用于类和方法,作用是用它修饰的变量不会参与序列化操作(不能被序列化);
public class Main20 {
public static void main(String[] args) {
StringBuffer stringBuffer=new StringBuffer("hello");
System.out.println(stringBuffer);//实际上调用的是StringBuffer的toString()方法 打印结束hello
StringBuffer stringBuffer1=new StringBuffer(5);
System.out.println(stringBuffer.capacity());//这里打印出来的结果是5
StringBuffer stringBuffer2=new StringBuffer();
//问题,如果是采用默认构造和指定长度的构造StringBuffer又该如何赋值呢?
}
}
问题,如果是采用默认构造和指定长度的构造StringBuffer又该如何赋值呢?
API(方法) | 作用 | 案例 |
StringBuffer append(任意数据类型) | 向StringBuffer中追加字符串,拼接的意思 | stringBuffer.append("world"); helloworld |
int | 返回当前的容量 | 值是"hello",采用的是默认构造,stringBuffer.append("helloworld");返回是StringBuffer中数组的长度 返回长度16 |
int length() | 返回当前的字符串有效字符的个数 | 值是"hello" 返回结果5 |
StringBuffer delete(int start,int end) | 删除指定区间的字符串,包含开始,不包含结束 | "helloworld" 删除world--stringBuffer.delete(5,stringBuffer.length()); |
StringBuffer deleteCharAt(int index) | 删除指定下标的元素 | "helloworld" 删除w stringBuffer.deleteCharAt(5); |
char charAt(int index) | 返回指定下标的元素 | "helloworld".charAt(1) 返回e |
indexOf(String str) | 和String的indexOf的功能一样 | stringBuffer.indexOf("wo1") 找到返回下标,找不到返回-1 |
insert(int index,String str) | 在指定的位置插入一个新的内容,在指定位插入了新内容后,指定位后的原内容自动后移 | stringBuffer.insert(5,"--") hello--world |
replace(int start,int end,String str) | 替换指定区间内的内容,包含开始,不包含结束 | stringBuffer.replace(4,7,"ABC")"helloworld"改变后hellABArld |
void setCharAt(int index,char ch) | 改变某一个下标位的元素值 | stringBuffer.setCharAt(5,'W')System.out.println(stringBuffer); |
reverse() | 将字符串的排序顺序,倒序排列,影响内部存储 | stringBuffer.reverse() |
String substirng(int Start) | 与String中的substring一样,从指定位截取到最后,返回一个String | String str=stringBuffer.substring(5); world |
String substring(int start , int end) | 包含开始,不包含结束,返回一个String | String str=stringBuffer.substring(4,7); owo |
//System.arraycopy(要取值的数组,从哪个下标开始取,复制接收的数组, 接收的数组从哪个下标开始接收,取值数组要取多少个元素);
System.arraycopy(ch2,0,ch1,5,3); //将ch2中的内容复制到ch1中
public class Main {
public static void main(String[] args) {
char []ch1=new char[10];
ch1[0]='h';ch1[1]='e';ch1[2]='l';ch1[3]='l';ch1[4]='o';
char []ch2={'w','o','r','l','d'};
//System.arraycopy(要取值的数组,从哪个下标开始取,复制接收的数组, 接收的数组从哪个下标开始接收,取值数组要取多少个元素);
System.arraycopy(ch2,0,ch1,5,3);
}
}