java基础(个人笔记)

JAVA简介

JVM:java虚拟机
JRE:Java 运行时环境,包含java虚拟机(jvm)+JavaSE标准类库,如果只需要运行java程序,下载并安装JRE即可
JDK:Java 开发工具包,JDK 包含了JRE。
javac:编译器,将源程序转成字节码
java:运行编译后的java程序(.class后缀的)
在这里插入图片描述
java平台:JavaSE 开发桌面程序,JavaEE 开发Web程序,JavaME开发安卓程序

一、IDEA快捷键

快捷输出:sout System.out.println();
快捷主方法:psvm
fori for循环
itli List迭代
快捷复制当前行到下一行:ctrl+d
快捷导入包:Alt+Enter
快捷换行或者添加分号:ctrl+shift+回车
快捷生成get,set方法和toString:alt+insert
快捷查看源码:Ctrl+鼠标左键
单页面查找某个属性或者单词:ctrl+f
接上面修改选中的单词: ctrl+R
撤销操作:ctrl+z
全局查找替换:ctrl+shift+f/r
快速查找类或者文件:ctrl+shift+n
模糊查找,查找所有:ctrl+shift+a
IntelliJ IDEA生成get/set有2种方式: alt+insert
快速左右移动光标:ctrl+←→方向键
单行注释:ctrl+/
多行注释/**/:ctrl+shift+/
自动接受变量:ctrl+回车

二、数据类型和变量

数据类型分为基本数据类型和引用数据类型(数组和类,接口等)
字符串String不属于基本数据类型,他是类
整型:
byte(字节型,1字节)
short(短整型,2字节)
int(整型,4字节)
long(长整型.8字节)
浮点型:
float(单精度浮点型,4字节) 类型定义时在后面加F: flaot a=30F;
double(双精度浮点型,8字节) 默认double,也可以加D
字符型:
char(字符型,2字节) 类型定义时加单引号 char a=‘高’
布尔型:
boolean (1字节) 只能定义true和false
转义字符:/ 开头的
类型转换:
箭头指向的方向是可以自动转换,相反方向需要强制类型转换,虚线表示精度可能会丢失。强制类型转换会丢失数据
在这里插入图片描述

java1.7版本后数字之间可以加下划线没有任何影响
比如:int money=10_0000_0000;

1B(byte,字节)=8(bit,位)位是计算机储存最小单位,字节是计算机处理数据的基本单位,一个汉字为两个字节,一个英文字母为一个字节,一个字节可以存放最大数是255
1kb=1024B
1m=1024kb;
1G=1024m
例题:
long a=123456789L;
int b=123,i=200;
short c=10;
byte d=8;
a+b+c+d //结果是long型
b+c+d //结果是int型
b/(double)i //结果是double型

三、变量命名规范:

类成员:首字母小写加驼峰原则
常量:大写字母加下划线
类名:首字母大写加驼峰原则;
方法:首字母小写加驼峰原则

包机制:一般用公司域名倒置作为包名:
例如www.baidu.com包名为com.baidu.www

四、运算符

与&& 或|| 非!
int a=10,b=20;
Systm.out.println(“”+a+b);//输出结果1020
Systm.out.println(a+b+“”);//输出结果30,原因是运算顺序不同

int a=80;
String type=a>60?“及格”:“不及格”;
Systm.out.println(type);//输出结果为及格

boolean flag=true;
if(flag){}和if(flag==true){}这两个判断是完全相等的

Animal a=new Animal("花花",2);
Animal b=new Animal("花花",2);
boolean flag=a.equals(b);//输出结果为false,比较地址
System.out.println(a==b);//输出结果为false

String a=new String("hello");
String b=new String("hello");
flag=a.equals(b);//输出结果为true,因为String类重写了equals方法,只是比较字符串内容
System.out.println(a==b);//输出结果为false

重写equls方法

//在Animal中重写equals方法,Animal中有name和Month属性
public boolean equals(Animal obj){
	if(obj==null)//传入参数为空直接放回false
		return false;
	//如果name和month相等返回true
	if(this.getName().equals(obj.getName())&&
	(this.getMonth()==obj.getMonth()))
		return true;
	else
		return false;
}

五、JavaDoc(生成自己的java文档)

方法一、 找到你要生成的类的保存路径,运行cmd控制台,打开项目文件夹运行并生成类文档输入:javadoc -encoding UTF-8 -charset UTF-8 生成的类名.java
回车运行后找到该项目文件夹打开后运行index.html
方法二、 用idea直接生成在这里插入图片描述
在这里插入图片描述

六、方法和排序

Scanner(用户交互)
Scanner scanner=new Scanner(System.in);
String str=scanner.nextLine();//从键盘接收String类型的字符
scanner.close();//关闭,IO流养成习惯,用完close

if(scanner.hasNextInt()){} //判断输入的是否是整数
while(scanner.hasNexDouble()) //循环判断输入的数是否是双精度的数
可变参数:
一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通参数必须在它之前声明。在指定参数类型后面加一个省略号(…)例如:

public static void main(String[] args) {
printMax(34,56,2,3,5);
printMax(new double[]{1,2,3});//可变参数本质就是一个数组
}
//比较大小,输出最大值
public static void printMax(double...numbers){
	if(numbers.length==0){
	System.out.println("参数错误");
	return;}
	//存最大值
	double result=numbers[0];
	//排序,比较大小
	for(int i=1;i<numbers.length;i++){
	if(numbers[i]>result){
	result=numbers[i];
		}
	}
	System.out.println("最大值为:"+result);
}

递归:

//传入的值为3,f(3)*f(2)*f(1),到f(1)时开始把值进行返回1,再返回2,再返回3
public static int f(int n){
	if(n==1){
		return 1;
	}else{
	return n*f(n-1);
	}
}

遍历数组

for(int i:arr){
System.out.println(i);
}

冒泡排序
比较相邻的元素。如果第一个比第二个大,就交换他们两个,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。重复上面的步骤,直到没有任何一对数字需要比较。

		int [] a= {45,23,56,78,15,89};
		int b;
		for (int i : a) {
			System.out.print(i+" ");
			
		}
		for(int i=0;i<a.length-1;i++) {
			for(int j=0;j<a.length-i-1;j++) {
				 if(a[j]>a[j+1]) {
					 b=a[j];
					 a[j]=a[j+1];
					 a[j+1]=b;
				 }
			}
		}
		System.out.println();
		for (int i : a) {
			System.out.print(i+" ");
		}

七、继承(extendx)和多态

1.子类继承父类需要重写所有抽象方法
2.如果父类重写构造方法子类也需要重写构造方法
this()调用无参构造方法,必须放在方法体的第一行。
静态成员生命周期:
类加载时产生,销毁时释放,生命周期长。
方法重写:
在满足继承关系的子类中,方法名、参数列表与父类相同,访问修饰符大于等于父类的方法。
方法重载:
在同一个类中,方法名相同,参数个数、顺序、类型不同的方法。
方法执行顺序:
在这里插入图片描述

单例模式:

目的:使得类的一个对象成为该类系统中的唯一实例
优点:在内存中只有一个对象,节省内存空间,避免频繁的创建销毁对象,提高性能。
缺点:扩展困难,如果实例化后的对象长期不使用,系统默认为系统垃圾进行回收,造成对象状态丢失。
定义:一个类有且仅有一个实例,并且自动实例化向整个系统提供
代码实现:

//饿汉式:类加载的时候直接初始化对象。空间换时间,线程安全
public class StringletonOne{
	//1.创建类中私有构造
	private StringletonOne(){
	}
	//2.创建该类型的私有静态实例
	private static StringletonOne instance=new StringletOne():
	//3.创建公有静态方法返回静态实例对象
	public static StringletonOne getInstance(){
	return instance;
	}
}
//懒汉式:类加载并不直接初始化,第一次使用时进行实例化。时间换空间,存在线程风险

public class StringletonTow{
	//1.创建类中私有构造
	private StringletonTow(){
	}
	//2.创建该类型的私有静态实例
	private static StringletonTow instance=null;
	//3.创建公有静态方法返回静态实例对象
	public static StringletonTow getInstance(){
	if(instance==null)
		instance=new StringletTow();
	return instance;
	}
}

多态

1.编译时多态:方法重载。
2.运行时多态:java运行时系统根据调用该方法的实例的类型来决定调用那个方法。

//向上转型,隐式转型,自动转型。父类引用指向子类实例,不能调用子类特有的方法
//父类的静态方法无法被子类重写,所以只能调用父类原有的静态方法
Animal a=new Cat();
//向下转型,强制类型转换,可以调用子类特有的方法
Cat b=(Animal)a;
//instanceof运算符,返回true,false。用于判断左边的对象是否是右边类的实例
if(a instanceof Cat)

**抽象类(abstract)**不允许实例化,可以通过向上转型,指向子类实例。
子类没有重写父类所有的抽象方法,则也要定义为抽象类。
抽象类可以没有抽象方法。
抽象方法所在的类一定是抽象类。
抽象方法不能被static,final,private所修饰。

**接口(interface)**中可以不写abstract关键字。
类实现(implements)接口时,需要去实现接口中所有的抽象方法,否则需要将该类设置为抽象类。
接口中可以设置常量,默认public static final。
default:接口中的默认方法,可以带方法体。default void a(){}
接口中的静态方法也可以带方法体。
接口也可以实现继承,并且可以多继承
接口和抽象类的区别
1.抽象类只能继承一次,但是可以实现多个接口
2.接口和抽象类必须实现其中所有的方法,抽象类中如果有未实现的抽象方法,那么子类也需要定义为抽象类。抽象类中可以有非抽象的方法
3.接口中的变量必须用 public static final 修饰,并且需要给出初始值。所以实现类不能重新定义,也不能改变其值。
4.接口中的方法默认是 public abstract,也只能是这个类型。不能是 static,接口中的方法也不允许子类覆写,抽象类中允许有static 的方法
内部类获取内部类对象实例,方式1:new 外部类.new 内部类。
方式2:外部类对象.new 内部类。
内部类可以直接使用外部类的成员。
可以使用外部类.this.成员的方式,访问外部类中同名的信息。
静态内部类中,只能直接访问玩不累的静态方法,如果需要调用非静态成员,可以通过对象实例。new 外部类.成员
可以通过外部类.内部类.静态成员的方式,访问内部类中的静态成员
匿名内部类匿名内部类适合创建那种只需要一次使用的类。最常用的创建匿名内部类的方式是创建某个接口类型的对象。

八、包装类和异常

在这里插入图片描述
所有包装类都用final修饰不能被继承
在这里插入图片描述
基本数据类型和包装类的转换
装箱

//1、自动装箱
int t1=6;
Interger t2=t1;
//2、手动装箱
Interger t3=new Interger(t1);

拆箱

自动拆箱
int t4=t2;
//手动拆箱
int t5=t2.intValue();
double t6=t2.doubleValue();

基本数据类型和字符串

int t1=2;
String t2=Interger.toString(t1);//基本类型转换字符类型
int t3=Interger.parseInt(t2);//字符串转为基本数据类型
int t4=Interger.valueOf(t2);//字符串转为基本数据类型

异常

在这里插入图片描述
ArithmeticException:数学运算异常,出现了除以零这样的运算
NumberFormatExeption:数字格式化异常,出现字符串不能转数字
ArrayIndexOutOfBoundsException:数组下标越界异常,超出数组下标
NullPointerException:空指针异常,使用了不存在或者未初始化的对象
ClassCastException:类型转换异常,进行向下转型时,无法完成正常转换
ArrayStoreException:数组中包含不兼容的值
InputMismatchException:输入格式错误异常
FileBotFoundException:文件未找到异常
异常概念
System.exit(1);//无条件终止程序运行
throw:有程序员主动抛出某种特定的异常
throws:表示通知方法调用者,使用该方法时,可能会发生那些异常
throw抛出异常对象的处理方案:
1.通过try…catch包含throw语句–自己抛自己处理
2.通过throws在方法声明出抛出异常异常类型–调用者自己处理,也可以继续上抛
自定义异常
定义一个类,去继承Throwable类或者其他的子类。
和通过throw new Exception(描述信息)不一样,这种操作适合临时或者应用频率不高的异常处理情况,而自定义异常是结合业务产生的类型,并设置其描述信息,更适合在项目中相对频繁出现的场景

九、字符串(String)

String常用方法

在这里插入图片描述

==和equals,hashcode区别

byte,short,char,int,long,float,double,boolean 他们之间的比较,应用双等号(==),比较的是他们的值。

在字符串中,==比较的是对象地址是否相等,equals()比较的是内容是否相等,hashcode比较的是hash值是否相等
在这里插入图片描述

字符串数组互转

String str=new String("JAVA编程基础");
//将字符串改为byte数组
byte[] arrs=str.getBytes("UTF-8");
//将数组转换为字符串
String str1=nwe String(arrs,"UTF-8");

StringBuilder

String和StringBuilder的区别:
  String具有不可变性,而StringBuilder不具备,所以当频繁操作字符串时,使用StringBuilder
StringBuffer和StringBuilder的区别:
  二者基本相似,StringBuffer是线程安全,StringBuilder则没有,所以性能略高
StringBuilder常用方法

//拼接字符串
StringBuilder str=new StringBuilder("你好");
/*方法一
str.append(',');
str.append("imooc!");
System.out.println(str);
*/
//方法二
System.out.println(str.append(',').append("imooc!"));
//替换字符串
System.out.println(str.replace(4,8,"MOOC"));//方法一,直接替换
System.out.println(str.delete(4,8).insert(4,"MOOC"));//方法二,先删除后插入

十、集合

在这里插入图片描述
集合和数组的区别:
数组的长度固定,集合长度可以动态扩展
数组只能存相同的类型的数据,集合可以存不同类型的数据
在这里插入图片描述

ArrayList常用方法:

在这里插入图片描述

List list=new ArrayList();
list.add("java");//添加元素
list.add("c++");
list.get(0);//输出元素jvaa,下标从0开始
list.size();//列表元素个数
list.remove(1);//删除下标为1的c++,另一种方式:list.remove("c++);

案例演示:

//Notice类四个属性int id;String title;String creator;Date createTime 
Notice notice1=new Notice(1,"欢迎","管理员",new Date());//生成公告
Notice notice2=new Notice(2,"在线","老师",new Date());
ArrayList list=new ArrayList();
list.add(notice1);//添加第一条公告信息,存储公告信息
//显示第一条公告的title的内容
System.out.println(((Notice)(list.get(0))).getTitle());
//在第一条公告后面插入一条新的公告
list.add(1,notice2);//1表示在下标为1的地方插入
notice1.setTitle("编辑")//修改title内容
list.set(0,notice1);//用set方法修改之前的noticel1的内容

HashSet常用方法和泛型的应用

Set set=new HashSet();
set.add("bule");//向集合添加元素
set.add("red");
Iterator it=set.iterator();//迭代器,专门遍历集合来使用
while(it.hasNxt()){//hasNex()检测集合是否还有下一个元素
	System.out.println(it.next());//返回集合中的下一个元素
}

案例演示:

//以下的代码可以加入泛型规定必须是Cat类型的。
//加入泛型的作用是用来代替类型的强制转换
//Cat类中三个属性String name;int month;String species
Cat huahua=new Cat("花花"12"短毛猫");
Cat fanfan=new Cat("凡凡"3"田园猫");
Set set=new HashSet();//Set<Cat> set=new HashSet<Cat>();
set.add(huahua);//添加到HashSet中
set.add(fanfan);
//加入泛型Iterator it=set.iterator();
Iterator it=set.iterator();//迭代器,专门遍历集合来使用
//遍历猫的信息,需要在Cat类中重写toString()方法,否则输出的是引用地址
while(it.hasNxt()){
	System.out.println(it.next());
}
/*************************查找信息*******************************/
boolean flag=false;
Cat c;
it=set.iterator();//每遍历一次最好重新定义一次
while(it.hasNext()){
c=(Cat)it.next();//如果假如了泛型就不用强转为Cat,直接c=it.next();
if(c.getName().equals("花花")){
	flag=true;
	break;
	}
}
if(flag){
		System.out.println(c);//输出花花的信息
}else{
		System.out.println(”没找到“);
}
/************************************************************/
//删除花花的数据,下面的方法需要加入泛型后才能使用
for(Cat cat:set){//遍历set集合的元素,把set中的元素存到cat中
//花花在cat.getName()中
	if("花花".equals(cat.getName())){
	set.remove(cat);
//因为在遍历查找数据时不允许删除数据,所以如果找到花花删除后就要跳出循环
	break;
}
}
//遍历set集合
for(Cat cat:set){
	System.out.println(set);
}
/************************************************************/
//删除多条数据
Set<Cat> set1=new HashSet<Cat>();
for(Cat cat:set){
//删除年龄小于5的数据
	if(cat.getMonth()<5){
	set1.add(cat);//把年龄小于5的数据存到set1集合中
	}
}
set.removeAll(set1);
/************************************************************/
//删除所有set集合的元素
boolean flag1=set.removeAll(set);//删除所有信息,返回true
if(flag1){
		System.out.println("全部删了");
	}else{
		System.out.println("删除失败");
		}
	}
}

自己定义类时,如果要判断添加的信息是否重复需要重写hashCode()和equals()

在这里插入图片描述
在这里插入图片描述

Map和HashMap

在这里插入图片描述

LinkedHashMap和hashMap和TreeMap的区别

共同点:
HashMap,LinkedHashMap,TreeMap都属于Map;Map 主要用于存储键(key)值(value)对,根据键得到值,因此键不允许键重复,但允许值重复。

不同点:

  1. HashMap里面存入的键值对在取出的时候是随机的,也是我们最常用的一个Map.它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。在Map中插入、删除和定位元素,HashMap 是最好的选择。
  2. TreeMap取出来的是排序后的键值对。但如果您要按自然顺序(字典顺序),那么TreeMap会更好。
  3. LinkedHashMap 是HashMap的一个子类,如果需要输出的顺序和输入的相同,那么用LinkedHashMap可以实现。
  4. LinkedHashMap是线程不安全的。

演示案例一:

/*********输入三组单词对应的注释存储到HashMap中****************/
Map<String,String> animal=new HashMap<String,String>();
Scanner scanner=new Scanner(System.in);//创建键盘输入对象
int i=0;
while(i<3){
	System.out.println("输入key值(单词)");
	String key=scanner.next();//对象调用方法执行输入
	System.out.println("输入value值(注释)");
	String value=scanner.next();
	animal.put(key,value);//把输入的值传入HashMap集合中
	i++;
}
/*********************遍历输出value(注释)的值***********************/
Iterrator<String> it=animal.values().itertator();
while(it.hasNext()){
		System.out.println(it.next()+" ");
}
/**********遍历输出key(单词)和value(注释)的值*******************/
//Set类有泛型,Entry类也有泛型
Set<Enty<String,String>> entrySet=animal.entrySet();
for(Enty<String,String> entry:entrySet){
	System.out.print(entry.getKey());
	System.out.println(entry.getValue());
}
/**************通过key(单词)找到value(注释)的值****************/
String strSearch=scanner.next();//输入查找的key
//用keySet()方法把animal对象的所有key值取出来存到keySet集合中
Set<String> keySet=animal.keySet();
for(String key:keySet){//遍历keySet集合的值依次存到key中
//如果输入的值等于key的值
	if(sreSearch.equals(key)){
	//get()方法:返回到指定键所映射的值
	System.out.print("找到了"+key+animal.get(key));
	}
}

演示案例二:商品信息管理

/****Goods类中有三个属性:String id;String name;double price;*********/
public String toString(){
//Goods类中重写了toString()方法,其他类new Goods类时自动执行toString()
	return "商品编号"+id+",商品名称"+name+",商品价格"+price;
}
/**************在主方法中添加商品信息****************/
Map<String,Goods> goodsMap=new HashMap<String,Goods>();
String goodsId="s00001";
String goodsName="冰箱"
String goodsPrice="5000";
Goods goods=new Goods(goodsId,goodsName,goodsPrice);
goodsMap.put(goodsId,goods);//将商品信息存到HashMpa集合中
//遍历goodsMap集合中的所有value值,输出商品信息
Iterator<Goods> itGoods=goodsMap.values().iterator();
while(itGoods.hasNext()){
	System.out.print(itGoods.next());
}

Iterator(迭代器)
Iterrator接口以统一的方式对各种集合元素进行遍历

//调用集合的iterator方法得到Iterator对象
Iterator<String> it=set.iterator();
while(it.hasNext()){//判断迭代器中是否还有下一个内容
	System.out.print(it.next()+" ")
}

List集合排序

使用Collections.sort()对list中的数据排序

List<Integer> list=new ArrayList<Integer>();
list.add(2);list.add(6);list.add(1);
Collections.sort(list);//排序
for(int a:list){
System.out.println(n+" ");}

用比较器Comparator对类型的名字进行排序

//比较器
//创建类NameComparator实现接口Comparator<T>,实现方法compare
public int compare(Car o1,Cat o2){
//按名字升序排序,调用cat类中的getName方法
String naem1=o1.getName();
String name2=02.getName();
//比较,name1>name2返回正整数,name1=name2返回0,name1<name2返回负数
int n=name1.compareTo(name2);//降序则改变name1和name2的位置
return n;
//上面是对String类型排序。如果对int类型的年龄排序,获取年龄后相减
int age1=o1.getMonth();int age2=o1.getMonth();
return age1-age2;//升序排序,降序排序改为age2-age1
}
//cat类有姓名,年龄,品种三个属性。在主函数中测试
Cat huahua =new Cat("huahua",5,"英国短毛猫");
Cat fanfan=new Cat("fanfan",2,"中华田园猫");
Cat maomao=new Cat("maomao",3,"英国短毛猫");
List<Cat> list=new ArrayList<Cat>();
list.add(huahua);list.add(fanfan);list.add(maomao);
Collections.sort(list,new NameComparator());
for(Cat cat:list){ System.out.println(cat);}

Comparable接口排序
此接口强行对实现他的每个类的对象进行整体排序。
这种排序被称为类的自然排序,类的compareTo方法称为自然比较方法。
集合调用Collection.sort进行排序,数组调用Array.sort排序。

//商品类实现了Comparable<商品类>接口,有编号,名称,价格属性。且实现了接口中的compareTo方法
public int compareTo(商品类 o){
//获取商品价格
double p1=this.getPrice();double p2=o.getPrice();
//取出double的整型部分
int n=new Double(p1-p2).intValue();
return n;
}
//在测试类中添加数据后,直接调用Collection.sort();传入商品的集合对象即可排序

Comparator和Comparable的区别
在这里插入图片描述
TreeSet
TreeSet<>是一个有序的集合,他支持自然排序和根据实现Comparator或Comparable接口进行排序。默认按字母升序排序。

当TreeSet中添加自定义类的对象时,实现Comparator
TreeSet a=new TreeSet<>(new 实现Comparator的类());
a.add(对象);之后添加对象后会自动排序

泛型

优点:不用进行强制类型转换,避免运行时异常的安全隐患

//泛型作为参数,想把泛型的子类也传进来
public void sellGoods(List<? extends Goods> goods){}
//父类引用
public void sellGoods(List<? super Goods> goods){}

十一、线程

进程:进程是资源分配的基本单位,它是程序执行时的一个实例,在程序运行时创建。
线程:比进程还要小的运行单位,一个进程包含多个线程。

创建线程方式一:继承Thread类

在这里插入图片描述
在这里插入图片描述

继承Thread类,重写run()方法来创建线程
在这里插入图片描述
注意:线程开启后不一定立即执行,由cpu调度执行

创建线程方式二:实现Runnable接口

  1. 只有一个方法run()
  2. Runnable是java中用以实现线程的接口
  3. 任何线程功能的类都必须实现该接口

实现Runnable接口,重写run()方法来创建线程
注意:启动实现Runnable接口的类需要创建Thread对象调用start()方法启动,也就是需要三步。
下面启动方法可以直接:new Thread(new TextThread3()).start();
在这里插入图片描述
推荐使用方法二:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

创建线程方式三:实现callable接口

实现callable接口,重写call()方法,call作为线程的执行体,具有放回值,并且可以对异常进行申明和抛出,使用start()方法来启动线程。使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
在这里插入图片描述

线程的生命周期

线程的状态:新建,可运行,运行,阻塞,终止。五种关系的相互转换状态称为生命周期
在这里插入图片描述

Thread类常用方法的使用⬇

sleep方法

try{
//在指定的毫秒内让正在执行的线程休眠(暂停执行)
	Thread.sleep(1000);//静态方法类名直接调用,停止1000毫秒
	}catch(){}

join方法
调用该方法的线程结束后其他线程才能执行

//线程MyThread继承了Thread类
MyThread mt=new MyThread();
try{
//join(1)里面可以加入毫秒参数,代表此线程执行一毫秒后其他线程才能执行
	mt.join();
	}catch(){}

在这里插入图片描述

线程的优先级

一、优先级可以用1-10表示,数字越大优先级越高,超过范围会抛出异常。
二、主线程也就是main()方法默认优先级为5。
三、设置优先级并不一定优先级高的先执行
优先级常量:
线程最高优先级10:MAX_PRIORITY
线程最低优先级1:MIN_PRIORITY
线程默认优先级5:NORM_PRIORITY

//线程MyThread继承了Thread类
MyThread mt=new MyThread();
//currentThread()获取当前线程,getPriority()获取线程的优先级
Thread.currentThread().getPriority();
//设置线程优先级为10也可以写成mt.setPriority(Thread.MAX_PRIORITY);
mt.setPriority(10);
mt.getPriority();//获取mt线程的优先级

同步(synchronized)

使用关键字synchronized来进行同步,不允许其他线程对此方法进行打断.
在这里插入图片描述
语句块的使用例子:
void abc(){
synchronized(this){//方法体内执行过程中不允许被打断
方法体
}
}

十二、多线程

为什么要使用线程池?
   1.反复创建线程开销大
   2.过多的线程会占用太多的内存
线程池的重要性:
   1.用少量的线程——避免内存占用过多
   2.让这部分线程都保持工作,且可以反复执行任务——避免生命周期的损耗
   3.加快响应速度
   4.合理利用CPU和内存
   5.统一管理
线程池适合应用的场景:
  服务器接受到大量请求时,使用线程池技术非常的合适,它可以大大减少线程的创建和销毁次数,提高服务器的工作效率
  实际上,在开发中,如果需要创建5个以上的线程,那么就可以使用线程池来管理

等待队列也已经排满了,再也塞不下新任务了同时,线程池中的max线程也达到了,无法继续为新任务服务。

这时候我们就需要拒绝策略机制合理的处理这个问题。

JDK拒绝策略:new ThreadPoolExecutor.AbortPolicy()

  • AbortPolicy(默认):直接抛出 RejectedExecutionException异常阻止系统正常运知。
  • CallerRunsPolicy:"调用者运行"一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。
  • DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务。
  • DiscardPolicy:直接丢弃任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种方案。
    以上内置拒绝策略均实现了RejectedExecutionHandler接口。

workQueue工作队列

直接交接:SynchronousQueue没有队列
无界队列:LinkedBlockingQueue无限队列
有界队列:ArryBlockingQueue经典队列
延迟队列:DelayedWordQueue
子任务队列:workStealingPool 1.8后加入

添加线程的规则

在这里插入图片描述
在这里插入图片描述

jdk线程池对比

创建线程池:Executors.线程池
newFixedThreadPool:核心线程自定义固定,使用无限队列,容易造成大量内存占用

newSingleThreadExecutor:核心线程数固定为1,和上面类似

newCachedTreadPool:核心线程为零,最大线程数无限,60s自动回收线程。会导致创建非常多的线程

newScheduledThreadPool:支持定时及周期性任务执行的线程池

正确的创建线程池的方法:根据不同的业务场景创建

线程池里的线程设定为多少比较合适?

  • CPU密集型(加密、计算hash等):最佳线程数为CPU核心数的1-2倍左右。
  • 耗时IO型(读写数据库、文件、网络读写等):最佳线程数一般会大于CPU核心数很多倍
  • 参考Brain Goetz计算方法:线程数=CPU核心数*(1+平均等待时间/平均工作时间)

停止、暂停、恢复线程池的方法

  • executtorService.shutdown()停止线程池,但仍然会执行线程,直到所有线程执行完毕。停止状态。此时提交新任务到线程池会报错
  • executtorService。isShutdown()返回ture和false,判断线程池是否在停止状态
  • executtorService.isTerminated()返回ture和false,判断线程池是否彻底停止,所有线程已执行完毕
  • executtorService.awaitTerminnation(时间),判断在一定时间内线程池是否已彻底停止,返回ture和false
  • executtorService。shutdownNow()立刻彻底关闭线程池且会返回队列中的线程

线程池状态

线程池组成部分:线程池管理器,工作队列,工作线程,任务接口
在这里插入图片描述

自定义线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyThreadPoolExecutorDemo {

	public static void doSomething(ExecutorService executorService, int numOfRequest) {
	    
        try {

            System.out.println(((ThreadPoolExecutor)executorService).getRejectedExecutionHandler().getClass() + ":");
            TimeUnit.SECONDS.sleep(1);

            for (int i = 0; i < numOfRequest; i++) {
                final int tempInt = i;
                executorService.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "\t 给用户:" + tempInt + " 办理业务");
                });
            }
            
            TimeUnit.SECONDS.sleep(1);
            System.out.println("\n\n");
            
        } catch (Exception e) {
        	System.err.println(e);
        } finally {
            executorService.shutdown();
        }
	}
	//创建线程池ThreadPoolExecutor
	public static ExecutorService newMyThreadPoolExecutor(int corePoolSize,
           int maximumPoolSize, int blockingQueueSize, RejectedExecutionHandler handler){
		return new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                1,//keepAliveTime
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(blockingQueueSize),
                Executors.defaultThreadFactory(),
                handler);
	}
	public static void main(String[] args) {
		doSomething(newMyThreadPoolExecutor(2, 5, 3, new ThreadPoolExecutor.AbortPolicy()), 10);
		doSomething(newMyThreadPoolExecutor(2, 5, 3, new ThreadPoolExecutor.CallerRunsPolicy()), 20);
		doSomething(newMyThreadPoolExecutor(2, 5, 3, new ThreadPoolExecutor.DiscardOldestPolicy()), 10);
		doSomething(newMyThreadPoolExecutor(2, 5, 3, new ThreadPoolExecutor.DiscardPolicy()), 10);
	}

}

```c
class java.util.concurrent.ThreadPoolExecutor$AbortPolicy:
pool-1-thread-1	 给用户:0 办理业务
pool-1-thread-3	 给用户:5 办理业务java.util.concurrent.RejectedExecutionException: Task com.lun.concurrency.MyThreadPoolExecutorDemo$$Lambda$1/303563356@eed1f14 rejected from java.util.concurrent.ThreadPoolExecutor@7229724f[Running, pool size = 5, active threads = 0, queued tasks = 0, completed tasks = 8]

pool-1-thread-2	 给用户:1 办理业务
pool-1-thread-5	 给用户:7 办理业务
pool-1-thread-3	 给用户:3 办理业务
pool-1-thread-4	 给用户:6 办理业务
pool-1-thread-1	 给用户:2 办理业务
pool-1-thread-2	 给用户:4 办理业务
class java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy:
pool-2-thread-1	 给用户:0 办理业务
pool-2-thread-2	 给用户:1 办理业务
pool-2-thread-1	 给用户:2 办理业务
pool-2-thread-3	 给用户:5 办理业务
pool-2-thread-3	 给用户:7 办理业务
pool-2-thread-3	 给用户:9 办理业务
pool-2-thread-4	 给用户:6 办理业务
pool-2-thread-2	 给用户:3 办理业务
pool-2-thread-5	 给用户:8 办理业务
main	 给用户:10 办理业务
pool-2-thread-1	 给用户:4 办理业务
pool-2-thread-3	 给用户:11 办理业务
pool-2-thread-4	 给用户:13 办理业务
main	 给用户:14 办理业务
pool-2-thread-1	 给用户:12 办理业务
pool-2-thread-5	 给用户:15 办理业务
pool-2-thread-2	 给用户:17 办理业务
main	 给用户:18 办理业务
pool-2-thread-3	 给用户:16 办理业务
pool-2-thread-4	 给用户:19 办理业务



class java.util.concurrent.ThreadPoolExecutor$DiscardOldestPolicy:
pool-3-thread-1	 给用户:0 办理业务
pool-3-thread-2	 给用户:1 办理业务
pool-3-thread-1	 给用户:2 办理业务
pool-3-thread-2	 给用户:3 办理业务
pool-3-thread-3	 给用户:5 办理业务
pool-3-thread-5	 给用户:8 办理业务
pool-3-thread-2	 给用户:7 办理业务
pool-3-thread-4	 给用户:6 办理业务
pool-3-thread-1	 给用户:4 办理业务
pool-3-thread-3	 给用户:9 办理业务



class java.util.concurrent.ThreadPoolExecutor$DiscardPolicy:
pool-4-thread-1	 给用户:0 办理业务
pool-4-thread-2	 给用户:1 办理业务
pool-4-thread-1	 给用户:2 办理业务
pool-4-thread-2	 给用户:3 办理业务
pool-4-thread-3	 给用户:5 办理业务
pool-4-thread-3	 给用户:9 办理业务
pool-4-thread-1	 给用户:4 办理业务
pool-4-thread-5	 给用户:8 办理业务
pool-4-thread-4	 给用户:6 办理业务
pool-4-thread-2	 给用户:7 办理业务






## ThreadLocal
1.2个线程分别用自己的SimpleDateFormat,这没问题
2.后来延伸出10个
3.但是当需求变成了1000个,那么必然要用线程池。每个线程都new开销也大
4.所有线程都共用一个SimpleDateFormat对象,这是线程不安全的出现了并发安全问题
5.可以选择加锁,但是效率低
6.更好的解决方案式使用ThreadLocal,线程安全,每个线程独有自己的对象,不需要加锁
![在这里插入图片描述](https://img-blog.csdnimg.cn/02ec84a3d6c9417c9bb07440f83112a6.png)
![在这里插入图片描述](https://img-blog.csdnimg.cn/079f689ae51148c5ad562516ef29691a.png)
**需要注意,线程或者线程池没关闭ThreadLocal创建的对象会一直存在,会造成内存泄漏**
约定:使用完ThreadLocal之后应该调用remove方法
## 十三、反射
反射是再运行时动态访问类的属性和方法的技术。 	
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200525160948560.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyMDEyODUz,size_16,color_FFFFFF,t_70)

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200514091740272.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyMDEyODUz,size_16,color_FFFFFF,t_70)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200514092536188.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyMDEyODUz,size_16,color_FFFFFF,t_70)
**Constructor类(操作构造方法)**
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020052516270755.png)
**Field类(操作属性)**
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200525164256480.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyMDEyODUz,size_16,color_FFFFFF,t_70)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200514092438541.png)
System.out.print(c5.getName());//获得包名加类名
System.out.print(c5.getSimpleName());//获得类名
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200514092710614.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQyMDEyODUz,size_16,color_FFFFFF,t_70)
## 十四、javaIO流
file类常用方法:
file.isDirectory()//判断是否是目录,返回true或false
file.isFile()//判断是否是文件,返回true或false
三种创建File对象的方法

```java
/****************三种创建File对象的方法**************************/
File file=new File("c:\\imooc\\io\\student.txt");//方式一
File file=new File("c:\\imooc","io\\student.txt");//方式二
File file=new File("c:\\imooc");
File file1=new File(file,"io\\student.txt");
/****************	创建目录**************************/
File file=new File("c:\\imooc\\io\\set\\HashSet");
if(!file.exists()){//exists方法判断文件是否存在,如果不存在就新建
	file.mkdirs();//mkdirs()创建多级目录,mkdir()创建一级目录
	
}
/*****创建文件************/
if(!file.exists()){//判断是否存在文件
file.createNewFile();//创建文件,需要处理异常
}

FileInputStream(文件输入流)

在这里插入图片描述

FileInputStream(文件输入流):从文件系统的某个文件中获得输入字节流。
为了保证close()方法一定执行,通常把关闭流的操作写在finally代码块中
在这里插入图片描述

try{
FileInputStream fis=nwe FileInputStream("imooc.txt");//找到文件保存到fis
int n=0;
//方法一:遍历文件内容,如果==-1代表遍历输出完毕
while((n=fis.read())!=-1){
System.out.println((char)n);//因为n是int型需要强转为char型,否则输出的是数字
}
}
catch(FileNotFoundExcption e){
e.printStackTrace();}catch(IOExcepion e){e.printStackTrace();}
fis.close();//记得关闭输入流
/****************方法二**************************/
FileInputStream fis=nwe FileInputStream("imooc.txt");
byte[] b=new byte[100];
fis.read(b);
System.out.println(new String(b));//强转为字符串
fis.colse();

FileOutputStream(文件输出流):

//输出信息到文件
try{
FileOutputStream fos=nwe FileOutputStream("imooc.txt");//覆盖内容
FileOutputStream fos=nwe FileOutputStream("imooc.txt"true);
fos.write('a');
fos.close();
}
catch(FileNotFoundExcption e){
e.printStackTrace();
}catch(IOExcepion e){e.printStackTrace();}

文件拷贝,复制图片
在这里插入图片描述

缓冲输入输出流(BufferedOutputStream和BufferedInputStream)

在这里插入图片描述

在这里插入图片描述

字节字符转换流

在这里插入图片描述
其他字符流(复制)
在这里插入图片描述
在这里插入图片描述

对象序列化

在这里插入图片描述
在这里插入图片描述

十五、JVM

java源码编译后变成.class文件

new出来的对象地址不同,但是class loader都是相同的

双亲委派机制:类名和java环境自带的类名相同,那么优先执行自带的类。作用是安全

new Robot()可以实现鼠标按下和键盘的操作

native:有native标识的类表示本地方法栈,说明java的作用范围达不到了。会调用本地方法接口(JNI)

JNI:扩展java的使用,融合不同的编程语言为java所用

什么是反射?
java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能叫做java语言的反射机制
什么是ClassLoader?
ClassLoader的作用是从系统外部获得Class二进制数据流装载进系统,然后交给Java虚拟机进行连接、初始化等操作。它是java的核心组件,所有的Class都是由他进行加载的。
ClassLoader的种类:
1.BootStrapClassLoader:C++编写,加载核心库java.*
2.ExtClassLoder:java编写,加载扩展库javax.*
3.AppClassLoader:java编写,加载程序所在目录
4.自定义ClssLoader:定制化加载

十六、Lambda表达式

在这里插入图片描述
函数式接口中有方法需要重写才能使用可以使用lambda表达式。
函数式接口:有且只有一个抽象方法得接口

//返回a*b结果,重写了opcratc方法
//如果参数只有一个可以省略小括号
MathOperation mult=(a,b)->a*b+0f;
System.out.println(mult.opcratc(5,3));

函数式编程

函数式编程是基于函数式接口并使用lambda表达式的编程方式
函数式编程理念是将代码作为可重用数据代入到程序运行中
jdk8后提供了一系列的函数式接口,位于java.utl.function
编写函数式接口可以添加@FunctionalInterface通知编译器这是函数式接口,进行抽象方法检查。起到检查无其他作用

下面方法中Predicate是新增的函数式接口,用于测试传入的数据是否满足判断要求。test方法返回的是boolean值
在这里插入图片描述
在这里插入图片描述
例子
在这里插入图片描述
在这里插入图片描述

Stream流式处理

在这里插入图片描述

//获取集合中最大的偶数
Optional<Integer> op=Arrays.asList(1,2,3,4,5,6).stream()
	.filter(x->x%2==0)  //获取偶数
	.sorted((a,b)->b-a) //排序,从大到小
	.findFirst();  //取第一个

在这里插入图片描述
在这里插入图片描述

例子
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

十七、注解和反射

元注解的作用就是负责注解其他注解

  • @Target:描述注解使用范围(被描述的注解可以用在什么地方)
  • @Retention:表示需要在什么级别保存该注释信息,用于描述注解生命周期
  • @Documented:说明该注解将生成在javadoc中
  • @Inherited:表示子类可以继承父类的注解

自定义注解:

  1. 使用@interface用来声明一个注解public @interface ForUpdate {自定义内容}
  2. 其中的每一个方法实际上申明了一个配置参数
  3. 方法名称就是参数的名称,返回值类型就是方法的类型(返回值只能是基本类型,Class,String,enum)
  4. 注解元素必须有值,可以通过default来声明参数的默认值,如果只有一个参数成员,一般参数名为value。

反射(Reflection)

  • 反射机制允许程序在执行期通过反射api取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
  • 一个类在内存中只有一个class对象
  • 一个类被加载后,类的整个结构都会被封装在class对象中

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
常量因为加载是就已经初始化到方法区中,所以不需要初始化类去调用

反射加自定义注解记录更改日志

package com.zrantech.config;
import java.lang.annotation.*;
/**
 * 描述:自定义注解标记那些字段需要记录日志
 */
@Target(ElementType.FIELD) //注解范围为属性
@Retention(RetentionPolicy.RUNTIME) //作用在程序运行时
@Documented //在javadoc文档中记录
public @interface ForUpdate {
    //默认为空字符串,只有一个字段推荐名字fieldName改为value
    String fieldName() default "";
}
package com.zrantech.utils;
import com.zrantech.config.ForUpdate;
import com.zrantech.hospital.entity.ChangeLog;
import javax.persistence.Table;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 描述:实现记录Bean修改明细,判断是否需要记录日志
 */

public class BeanUpdateUtils {
    private static Pattern humpPattern = Pattern.compile("[A-Z]");
    /**
     * 描述:驼峰转下划线
     * @Author: GXY
     * @date 2022/4/24
     **/
    public static String changeUpperToUnderLetter(String para){
        String regExp="([A-Z]{1})"; // 匹配单个字符
        Pattern pattern = Pattern.compile(regExp);
        Matcher matcher = pattern.matcher(para);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            // 把大写的 改成 _小写的内容。匹配大写后改成小写的,前面加一个下划线
            matcher.appendReplacement(sb, "_"+matcher.group().toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     * 获取变更内容,返回需要保存的日志
     * @param newBean 更改前的Bean
     * @param oldBean 更改后的Bean
     * @param userId 修改人id
     * @param id 字段唯一主键
     * @param <T>
     * @return  list
     */
    public static <T> List getChangedFields(T newBean, T oldBean,String userId,String id,int userType){
        List list=new ArrayList<>();
        //获取newBean的所有属性存到数组中
        Field[] fields = newBean.getClass().getDeclaredFields();
        ChangeLog changeLog;
        for(Field field : fields) {
            //设置可以获取变量名 和 变量的值.
            field.setAccessible(true);
            //判断注释ForUpdate是否在此field字段上
            if (field.isAnnotationPresent(ForUpdate.class)) {
                try {
                    //获取属性值
                    Object newValue = field.get(newBean);
                    Object oldValue = field.get(oldBean);
                    //判断两个对象值是否相等,不相等才保存
                    if(!Objects.equals(newValue, oldValue)) {
                        //保存
                        changeLog=new ChangeLog();
                        changeLog.setTableName(newBean.getClass().getAnnotation(Table.class).name());
                        changeLog.setUpdateTime(new Date());
                        changeLog.setChangeField(changeUpperToUnderLetter(field.getName()));
                        changeLog.setBeforeChange(newValue.toString()); //更改前
                        changeLog.setAfterChange(oldValue.toString());//更改后
                        changeLog.setPrimaryKey(id);
                        changeLog.setUpdateUserId(userId);
                        changeLog.setUserIdType(userType);
                        changeLog.setText(field.getAnnotation(ForUpdate.class).fieldName());
                        list.add(changeLog);
                    }
                } catch (Exception e) {
                    System.out.println(e);
                }
            }
        }
        return list;
    }
}
package com.zrantech.hospital.entity;


import com.zrantech.base.comment.Comment;
import com.zrantech.base.entity.BaseEntity;
import com.zrantech.config.ForUpdate;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;

/**
 * 病人建档实体类
 * @author zuguo
 */
@Entity
@Table(name = "his_patient")
@Data
@NoArgsConstructor
@Comment("患者建档表")
public class Patient extends BaseEntity {

    @Id
    @Comment("主键")
    @GeneratedValue(generator = "system-uuid")
    @Column(length = 35)
    private String id;
    @Comment("建档类型_见字典_1-成人 2-儿童")
    @Column(length = 50)
    private String setupType;
    @Comment("证件类别_见字典 证件类型")
    @Column(length = 50)
    private String idType;
    @Comment("证件号_为空时是临时卡")
    @Column(length = 20, unique = true)
    private String idNo;

    @Comment("门诊号,患者医院唯一编号,his返回")
    @Column(length = 50, unique = true)
    private String patientId;

    @Comment("病人类别_见字典_自费,医保")
    @Column(length = 50)
    private String patientType;
    @Comment("姓名")
    @Column(length = 20)
    private String patientName;
    @Comment("民族_见字典")
    @Column(length = 50)
    private String nation;
    @Comment("婚姻_见字典")
    @Column(length = 50)
    private String marriage;
    @Comment("性别_1男,2女,3其他")
    @Column(length = 50)
    private String sex;
    @Comment("出生日期_格式:yyyy-MM-dd")
    @Column(length = 12)
    private String birthday;
    @ForUpdate(fieldName = "联系电话")
    @Comment("联系电话")
    @Column(length = 15)
    private String phone;

    @Comment("年龄")
    @Column(length = 3)
    private Integer age;

    @ForUpdate(fieldName = "详细地址")
    @Comment("详细地址")
    private String address;

    @ForUpdate(fieldName = "家庭地址")
    @Comment("家庭地址")
    private String homeAddress;

    @Comment("家庭地址编码")
    @ForUpdate(fieldName = "家庭地址编码")
    private String homeAddressCode;

    @Comment("监护人姓名")
    private String guardianName;
    @Comment("监护人身份证号")
    private String guardianIdNo;
    @Comment("监护人联系电话")
    private String guardianPhone;

    @Comment("指派的客服id,关联sys_user表的id,非会员")
    private String customerId;

    @Comment("指派的健康管家id,关联sys_user表的id,会员")
    private String managerId;

    @Comment("指派的健康管家所属的组id")
    private String groupId;

    @Comment("是否能评价")
    @Column(columnDefinition = "bit DEFAULT 0")
    private boolean evaluate;

    @Comment("证件类型,字典")
    private String cardType;
}

十八、代码规范

1.Map/Set的key为自定义对象时,必须重写hashCode和equals
解释:Set hashSet = new HashSet();
hashSet.add(new ObjDemo(“1”,“对象1”));
hashSet.add(new ObjDemo(“1”,“对象1”));

我们逻辑上放进去的是一个对象,因为id、name都相等。但是会发现在hashSet中却有两个对象,因为这两个对象的内存地址是不同的。所以要重写equals()和hashCode()方法。

@Override
public boolean equals(Object o) {
	if (o instanceof ObjDemo){
		ObjDemo obj = (ObjDemo)o;
		return id.equals(obj.id);
	}
	return false;
}

@Override
public int hashCode() {
	return Objects.hash(id);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值