- 1.环境
JDK ---- java development Kit(JAVA 开发工具包)---编写java程序员用的软件
JRE ---- java Runtime Evironment(java 运行环境) --- 运行java时用户使用的软件
SE -- 标准版 -简单服务器java平台
EE -- 企业版 -复杂服务器java平台
ME -- 微型版 -小型服务器java平台
SDK ---- Software Development Kit(软件开发工具包) -- 软件开发工具包
- 2.数据类型
大小为2的 (x位-1)次方 首位表示正负号
int 32位 4字节(Byte) -20亿~20亿左右 2^31
short 16位 2字节 -3W~3W左右 2^15
long 64位 8字节
byte 8位 1字节 -128~127
float 32位 4字节 6.7位有效数字
double 64位 8字节 精度是float的两倍 15位有效数字
char 16位 2字节 可存放汉字 ,加减运算等会转换成ASCII码上对应的int字符
BigDecimal (翻译 大十进制)高精度大浮点数
BigInteger 任意精度整数
add(BigInteger/BigDecimal a) +
subtract(BigInteger/BigDecimal a) -
multiply(BigInteger/BigDecimal a) *
divide(BigInteger/BigDecimal a) /
mod(BigInteger/BigDecimal a) 余数
sqrt() 平方根
final --- 只能被赋值一次
<< 左移 --- 例: 1<<3 = 8 --- 1左移3位 --- 0000 0001 → 0000 1000 =8
String 常用 API
1 s.substring(0,3) 截0-3的字符串
2 s.equalsIgnoreCase() 不区分大小写判断是否相等
3 char s.charAt(1) 返回第1个char值 : 例:hello.charAt(1) = "e"
4 String[] s.split(",") “,”分割 List<String> list = Arrays.asList(s.split(","))→ ","分割后转成List<>
5 int x.compareTo(String y) 按照字典顺序,返回 x-y; x小返回一个负数,反之正数,相等为0
6 boolean isEmpty() 是否为空---String的方法里就判断了String.length==0
7 int length() 长度
8 String replace(String old,String new) 将old替换成new --- 例:hello.replace("o","a") → hella
9 String trim() 去掉收尾空白符(空格,换行等)
10 int indexOf(String str,int from) 返回从from开始,找到最后一个str的位置 没找到返回-1 --
例:hello.indexOf("o",1) 从1开始找o(e开始) 返回=4
StringBuilder String构建器
经常要改String时得用StringBuilder
StringBuilder 常用 API
StringBuilder b=new StringBuilder()
1 StringBuilder append(String add) 添String --- 例:b.append("aaa"),b.append("b") b="aaab"
2 StringBuilder insert(int position,String str) 在position位置处添一个str
3 StringBuilder delete(int startPosition,int endPosition) 删除从startPosition到endPosition
数组
int[] a=new int[100] 数组一旦创建,就不能改变长度
int[] a={1,2,3,4};
数组排序 Arrays.sort(a) --这个方法使用了 “优化的快速排序算法”
快速排序算法:1.设定一个分界值 将小的放左边,大的放右边。在左边这个堆里 再设置一个分界值,
然后将小的放左边,大的放右边,一直递归直到排好顺序。
要调用Arrays.sort()进行排序,必须实现 Comparable接口。
例子: main(){
var staff=new Empoyee()[3];
staff[0]=new Empoyee("王",1100);//姓名,id
staff[1]=new Empoyee("张",4500);
staff[2]=new Empoyee("丽",1200);
Arrays.sort(staff);//根据id对人进行排序
}
public Empoyee implement Comparable<Empoyee>{ //普通javaBean + 实现Comparable接口
String name;
int id;
public Empoyee(String name,int id){
this.name=name;
this.id=id;
}
public int compare(Empoyee other){ //接口里需要实现的方法,返回比较那个值
//x.compare(y) 都是 x-y x大就是正值,x小就是负值,相等为0
return Integer.compare(id,other.getId()); //各个包装类都有实现这个方法,直接调就行
}
}
xxx[] Arrays.copyOf(xxx[] a,int end)
xxx[] Arrays.copyOfRange(xxx[] a,int start,int end)拷贝
int binarySearch(xxx[] a,int start,int end ,xxx v)
(翻译 二进制搜索)在a数组中start-end中找v 找到返回第几个
看源码得:ArrayList 初始化new的时候,会赋值一个空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
然后add()的时候 如果length小于默认长度10 则将数组扩容成10 如果length+1 比现在的数组length大,那么扩容成:
int newCapacity = oldCapacity + (oldCapacity >> 1);
右移一位 扩容1.5倍。
- 3.对象与类
类:构造对象的模板或者蓝图。
封装:将数据和行为组合在一个包中,并对对象的使用者隐藏具体的实现方式。对象的数据=实例字段。
方法:操作数据的过程
var弱类型变量,只能用于局部变量!如果可以从变量初始值推导出类型,那么可以用var声明,
无需指定类型。
类之间的关系
·依赖 uses-a 使用了,调用 英文名知意思
·聚合 has-a 组成了
·继承 is-a
final:这样的字段必须在构造对象时初始化,并且不能在修改。
static:每个对象都有一个副本,但是static修饰的没有副本。 公用该对象。
所以静态方法不用new,无法新建副本 大家都公用。
方法参数
※※※Java总是采用按值调用,也就是说 方法参数的值是一个副本,也就是方法不能修改传给他的变量的值。
例:
int a=10;
add(a);
privite void add(int a){
a=3*a;
}
结果 a=10;
但是对象的引用却不同了!方法得到的是对象的引用的副本 但是副本=引用 都引用同一个对象都可以修改对象的值。
MyInt a=new MyInt(10);
add(a);
privite void add(MyInt a){
a=3*a;
}
结果 a=30;
●方法不能修改基本数据类型的参数
●方法可以改变对象参数的状态
●方法不能让一个参数引用一个新的对象
重载:一样的方法名,不一样的入参。 编译器匹配挑选调用哪个方法的过程叫做重载解析,解析时每个方法都有他们的 方法的签名。如indexOf(int)、indexOf(String,int)...但是返回值不是签名的一部分!
生成一个随机数:a=new Random().nextInt(100); Random() 生成随机数器 nextInt(int n) 生成一个
0 ~ n-1 的随机数
包
包名:网址反过来+工程名+类名 如:com.baidu.corejava.Employee
一个类可使用 自己包的所有类+ 其他包的共有类。
声明全名或者 import java.time.*;
jar文件
将应用打包时,你只希望向用户提供一个单独的文件,而不是包含大量类的文件。jar文件就为了这个设计的。一个jar文件可以包含类文件,也可以包含图像声音等。而且jar是压缩的,使用的是zip压缩格式。
创建jar:工具在jdk/bin 目录下
注释
/**
@param +参数备注
@return +返回备注
@throws +可能出现异常备注
@auther +作者
@see,link +条目
*/
类设计技巧
1.一定要保证数据私有
2.一定要对数据进行初始化
3.不要在类中使用过多的基本类型 -- 其他类替代类似的基本类
4.不是所有的字段都需要单独的字段访问器,更改器。
5.分解过多职责的类
6.类名方法名能体现职责
7.优先使用不可变的类 --- 多线程同时访问减少bug
- 4.继承
子类=派生类=孩子类 extends 超类=基类=父类
继承:子类可以增改查父类,但是不能删。
super与this引用不一样。super不是对象的引用,只是能访问父类方法的一个关键词。 this却是一个对象的引用 可以赋值可以操作。
子类构造器不能访问父类的私有字段,必须通过一个构造器来初始化这些私有字段,可以利用super来调用父类构造器,而且得第一句。
如果没有写super显示的调用父类的构造器,那么系统会隐式的调用父类没参数的构造参数。---也就是初始化一个类(new 等),会跑这个类的构造函数->父亲的构造函数->爷爷的构造函数。。。 一直到继承树的顶端,然后在从顶的类运行完事 在运行下面的 一层一层下来 最后再运行 调用的这个类。--------------所以子类没有用super,父类又没有无参数构造函数 那就会报错。
子类父类都有同名方法 然后调用这个方法时 自动匹配 就是多态
多态:一个对象变量可以指示多种实际类型的现象称为多态。
❤动态绑定:在运行时能够自动的选择适当的方法。
静态绑定:private/static/final/构造器 声明的 编译器直接能找到具体哪个
重载解析:同名方法编译器通过参数解析具体调用哪个
“is-a” 规则=替换原则,如果两个类能替换 那就应该被设计为继承关系。经理是员工 经理继承员工。
Employee e=new Manager(); √
总结方法的调用:编译器通过 签名(方法名和参数) 找具体方法,没有就找他的父类,没有就找他父类... 但是这样时间太长,所以虚拟机定义了一个 方法表 ,直接找表就行。
❤❤❤阻止继承!!!final类和方法:不允许扩展。 如果不想被继承那就用final,final字段,初始化以后就不能在改变值了。
有一种观点:没有足够的理由得使用多态性的方法 都应该声明为final,少让子类来捣乱,这样会系统减少开销(内联:只有一个方法,没有覆盖的多个选择,编译器会直接进行优化 运行速度加快)
强制类型转换
●只能在继承层次内才能强转
●强转之前应该用instanceof进行检查
if(ee instanceof Mannger){
Mannger a=(Mannger) ee;
}
●减少强转,要强转就说明设计不太合理,多用多态,少用强转
抽象类 abstract
❤继承树的祖先一般都用抽象而不具体实现。抽象就是只定义方法名和参数,让子类去实现它。
有抽象方法的类,也得标记抽象
抽象类不能被实例化---new
1.private 只能自己访问
2.public全部可以
3.protected 只能在同一个包中访问+继承
4.默认 自己+本包
Object
Object是java所有类的始祖。
在java中只有基本类型不是对象。对象的始祖都是Object。所有的数组都是Object。
equals
Ojb.equals() --- 判断引用是否相等。
如果两不是一个类 equals返回false
equals方法特性:
1.自反性:x.equals(x)=ture
2.对称性:x.equals(y)=ture //y.equals(x)=ture
3.传递性:x.equals(y)=ture //y.equals(z)=ture //x.equals(z)=ture
4.一致性:反复调用一个结果
5.x.equals(null) 永远是false
Arrays equals(a,b) ab长度相等,并且每个元素都相同
Object equals(a,b) 全是null=true 一个null 返回false 否则返回 a.equals(b)
hashCode
散列码是由对象导出的一个整型值(可以是负数),代表了存储地址。
null.hashCode() 报错!!! Objects.hashCode(null)=0
toString
自定义类toString 一般为类名+[ "name="+name+","+... ]
Object的toString为 对象类名+散列码(hashCode)
Class getClass() 返回包含对象信息的类对象。
String getName() 返回类名
Class getSuperClass() 返回他爹的对象
ArrayList 能够自动调整数组的容量。当容量满时在调用add 会自动创建一个更大的数组,然后将对象从小的数组拷到大的数组。
或者:list.ensureCapacity(100) 初始声明100大小的数组。或者new ArrayList<>(100);
List 基础类用List得用包装类
list.add(3) ---- > 直接add一个int 会自动转换成Integer ---> list.add(Integer.valueOf(3))这个过程叫做 自动装箱,自动包装
反过来 int a=list.get(i) ---- >会自动转换成Integer ---> int a=list.get(i).intValue(); 自动拆箱,自动解包装
int intValue() 返回int
String toString()
int paresInt(String s)/(String s,int radix) 字符串和进制
Integer valueOf(String s)
max(Object... args) --> Object... = Object[] -->max(3,4,5) 编译器会解析成max( new Object[]{3,4,5} )
枚举 enum
●enum Size{SMALL,MEDIUM} 枚举一般不需要构造器,如要要构造器也只能是私有的,用来初始化赋值。
Size[] value=Size.values(); value等于 value[] {Size.SMALL,Size.MEDIUM}
Size s=Enum.valueOf(Size.class,"SMALL") ---> s=Size.SMALL
Enum valueOf(Class enum,String name) 返回name的枚举常量
toString 返回常量名
int ordinal 返回位置
int compareTo(E other)
❤❤❤反射❤❤❤
能够分析类能力的程序称为反射。
反射是一种很强大很复杂的机制,可以用来:
●在运行时分析类的能力
●在运行时检查对象,例如:编写一个使用所有类的toString方法
●实现泛型数组操作代码
●利用Method对象(类似C++中的函数指针)
Class类
在程序运行期间,java运行时系统始终为所有对象维护一个 运行时类型标识。这个信息会追踪每个对象所属的类。虚拟机利用运行时类型信息选择要执行的方法。 保存这些信息的类名为Class。可以通过Object的getClass()方法来访问这个Class实例。
获得Class类对象的三种方法 ①②③
var aaa= new Random();
①Class cl = aaa.getClass();
String name = cl.getName();//得到带包名的全名 "java.util.Random"反过来可以用forName() 来反向取到Class
②Class c=Class.forName("java.util.Random");
③Class cl= Random.class; //T.class(直接加 .class 就行)
Class对象实际上表示的是一个类型,可能是类可能是其他,如 int.class
每个类都有唯一的Class对象
if(e.getClass()==Random.class) 可以通过 但是Random的子类却不能。
if(e instanceof Manager) (子类) 可以通过
●通过Class来获取对象。如果有一个Class对象,可以用它构造类的实例。调用getConstructor方法得到一个Constructor(构造器)对象,然后使用newInstance方法来构造一个实例。
如:Class c=Class.forName("java.util.Random");
Object obj=cl.getConstructor().newInstance();
Constructor getConstructor(Class c) 生成一个对象,描述有指定参数类型的构造器。
Object newInstance(Object params) 将params传递到构造器,来构造这个构造器声明类的一个新实例。
URL getResoure(String name) --- class.getResoure("aaa.txt")
InputStream getResoureAsStream(String name) --- class.getResoureAsStream("aaa.txt")
找到name处的资源,返回URL或者InputStream(输入流)
❤Field -(翻译)字段 || Methods -(翻译)方法 || Constructors -(翻译)构造器,构造函数
(全部获得)
● FieId[] getFields(); 返回类中所有的public字段
● FieId[] getDeclaredFields(); 返回类中所有字段
● void AccessibleObject.setAccessible(FieId[] fieIds,boolean flag) 批量设置访问权限
(单条获得)
● FieId getFields(String name); 返回 name的 public字段
● FieId getDeclaredFields(String name); 返回 name的 字段
● void setAccessible(boolean flag) 设置访问权限
● Object get(Object obj)返回obj对象中用这个Field对象描述的这个字段的值
● Object set(Object obj,Object newValue)设置这个字段的值
❤例子:
public static void main(String[] args) throws Exception {
//StudentBean(String id,String name,String ago)
StudentBean bean = new StudentBean("1","王","18");
// 得到一个字段
Field fieldName = bean.getClass().getField("name"); // name 是变量名
String a = fieldName.get(fieldName); //a = 王
fieldName.set(fieldName,"李") //将name值设置成"李"
//如果要访问私有字段
Field fieldName = bean.getClass().getDeclaredFields("name");
fieldName.setAccessible(true);// 添加访问权限,才能访问私有属性, 不然会报错
String a = fieldName.get(fieldName); //a = 王
}
● Method[] getMethods(); 返回public方法
● Method[] getDeclaredMethods(); 返回所有方法 --Declared(翻译)声明
● String getName();
● Class getDeclaringClass() -- (翻译)声明类 返回这个类
● Class[] getParameterTypes() -- 返回参数类型数组
● Class getReturnTypes() -- 返回返回值类型
● int getModifiers(); Modifier-(翻译)修饰词 返回一个int值 代表方法是public,还是privite
String a=Modifier.toString(class.getModifiers()) a=class的修饰词 public
●boolean isFinal(int modifiers)
●boolean isPublic(int modifiers)
●isAbstract、isNative、isPrivate...等等
● Constructors[] getConstructors(); 返回public构造函数
● Constructors[] getDeclaredConstructors(); 返回所有构造函数
●String getPackageName() 返回包名
●Class getType() 获得类型
●Class getComponentType() 获得数组组件类型
●boolean isPrimitive() 是否是基础类型
反射的应用:
public class TestController {
public static void main(String[] args) throws ReflectiveOperationException {
int[] test={1,5,7,8};
System.out.println(new ObjectAnalyzer().toString(test));
System.out.println("---------------------------");
var test1=new ArrayList<>();
test1.add(3);test1.add(8);test1.add(31);test1.add(1);
System.out.println(new ObjectAnalyzer().toString(test1));
System.out.println("---------------------------");
DeviceVO deviceVO=new DeviceVO();
deviceVO.setUserId("123456");
List<DeviceBean> deviceBeans=new ArrayList<>();
DeviceBean device=new DeviceBean();
device.setId("1");
device.setNickname("王");
device.setPassword("123");
device.setDeviceId("H0100001");
deviceBeans.add(device);
deviceVO.setData(deviceBeans);
System.out.println(new ObjectAnalyzer().toString(deviceBeans));
System.out.println("---------------------------");
System.out.println(new ObjectAnalyzer().toString(device));
}
}
打印结果:
int{[1,5,7,8]}
---------------------------
java.util.ArrayList{[java.lang.Integer{value=3},java.lang.Integer{value=8},java.lang.Integer{value=31},java.lang.Integer{value=1}]}
---------------------------
java.util.ArrayList{[com.hsh.modules.app.vo.DeviceBean{id=1,uuid=,ip=,port=,nickname=王,username=,password=123,type=,devId=null,deviceId=H0100001,deviceName=null,deviceLocation=null,location=null,detailLocation=null,siteId=null,deviceid=null}]}
---------------------------
com.hsh.modules.app.vo.DeviceBean{id=1,uuid=,ip=,port=,nickname=王,username=,password=123,type=,devId=null,deviceId=H0100001,deviceName=null,deviceLocation=null,location=null,detailLocation=null,siteId=null,deviceid=null}
/**
* @author
* @date 2021-03-30 17:23
* @desc 反射获得各种类型的值
*/
public class ObjectAnalyzer {
private List<Object> visited=new ArrayList<>();//标记数组,标记是否跑完了
//打印Objects的信息
public String toString(Object obj) throws ReflectiveOperationException{
if (obj == null) return null;
if (visited.contains(obj)) return "...";
visited.add(obj);
Class cl=obj.getClass();
if (cl == String.class) return (String)obj; //入参是String
/**
* 入参是数组
*/
if (cl.isArray()){
String r=cl.getComponentType() + "{[" ; //getComponentType 数组组件类型 = getType()
for (int i=0;i< Array.getLength(obj);i++){
if (i > 0) r=r+",";
Object val=Array.get(obj,i); //从列表取到第i条
if(cl.getComponentType() == String.class){
r=r+val;
}else if (cl.getComponentType().isPrimitive()){ //如果不是 基础类型
r=r+val;
} else {
r=r+toString(val);
}
}
return r+"]}";
}
/**
* 入参是ArrayList
*/
if (cl==ArrayList.class){
String r=cl.getTypeName() + "{[" ; //getComponentType 数组组件类型 = getType()
for (int i=0;i< ((ArrayList) obj).size();i++){
if (i > 0) r=r+",";
Object val=((ArrayList)obj).get(i); //从列表取到第i条
if(val.getClass() == String.class){
r=r+val;
}else if (val.getClass().isPrimitive()){ //如果 基础类型
r=r+val;
} else {
r=r+toString(val);
}
}
return r+"]}";
}
/**
* 入参是其他类型对象
*/
String r=cl.getName();
do {
r=r+"{";
Field[] fields=cl.getDeclaredFields(); //取到所有字段
AccessibleObject.setAccessible(fields,true);//设置访问权限 让private可以访问
//单条设置 field.setAccessible(true);
for (Field field:fields){//取到每条字段
if (!Modifier.isStatic(field.getModifiers())){ //Modifier翻译 修饰词
if (!r.endsWith("{")) r=r+","; //String.endsWith() 不是以什么结尾
Object val=field.get(obj);
r=r+ field.getName() + "=";
if(field.getType() == String.class){
r=r+val;
}else if (field.getType().isPrimitive()){//如果不是基础类型 String不是基础类型
r=r+val;
}else {
r=r+toString(field);
}
}
}
//如果{开头 也就是没进for循环if 里 全是静态
if (r.endsWith("{")){
r = r.substring(0,r.length() - 1);
}else {
r=r+"}";
}
cl=cl.getSuperclass();
}while (cl!=null);
return r;
}
}
继承的设计技巧
一.将公共操作和字段放在父类中
二.不要使用受保护字段
protected机制不能带来更多的保护
1.子类是无限的,可以从子类中访问protected字段,破坏封装性。
2.在Java中,只要在一个包中,protected字段就能访问,所以没啥用
三. 实现 is-a 关系
四.除非所有继承方法都有意义,否则不要使用继承
五.覆盖方法时,不要改变最初的设计想法
如:父类中的add是增加日期的,子类中覆盖的add是增加人员
六。使用多态,不要使用类型信息
例:
if(x instanceof ABean){
a(x);
}else if (x instanceof BBean){
a1(x);
}
如果x是这个类型调这个方法,如果这另一个类型调另一个方法,这样一个概念的方法应该整合成一个,
并将其放到父类或者接口中,然后利用 多态的动态分配机制去执行。
使用多态和接口更容易维护和扩展
x.a();(父类或者接口)
七.不要乱用反射
反射能够在运行时让我们查看方法字段,从而写出更通用的代码,但是这就绕开了编译器的监测,会让代码更脆弱。
- 5.接口
interface - (翻译)接口
implement - (翻译)实现
inner class - (翻译)内部类
接口不是类,而是对希望符合这个接口的类的一组需求。
接口默认都是 Public + abstract
1.接口不是类 所以不能直接new实例化。
x=new Comparable();//错
2.接口可以声明,并调用里面方法。
Comparable x;
x.compareTo();//对 (java里很多直接调用service的
ArcDataChangedEntity arcData = arcService.getNewestDeviceDetail(deviceId);)
3.接口可以用 instanceof 验证这个类有没有实现该接口
if(ABean instanceof Comparable)//对
4.接口也可以继承接口
public interface AppVersionMessageService extends IService< AppVersionMessageEntity> { }
5.接口中的字段只能是常量,接口中的变量默认自带 public static final;//我存常量就是这么干的
public interface ApiService {
String httpUrl = "www.hsh-iot.com/";
String httpUrl1 = "www.hsh-iot.com/111";
}
6.接口四舍五入就等于 父类的抽象方法,子类继承就会实现,为什么不这么干呢?
因为java只能extends一个父类,但是implement 可以实现多个接口。这样避免了多重继承的复杂和低效。
public abstract int compareTo(Obj o); //子类继承就得实现该方法
7.在java8中 接口中允许出现静态和私有方法。
不过一般做法是出一个接口的伴生类,将静态的私有的放伴生类中。如Collection/如Collections或Path/Paths。
8.默认实现可以用default修饰
对象的克隆(clone)
A a=new A();
A b=a;//b只是复制了a对象的引用,他两指向一个对象。这不是真正的克隆。
如果想要克隆一个对象,那就需要实现Cloneable接口(该接口只是一个标记接口,里面没有实现的方法,需要自己写clone())
public static void main(String[] strings) {
IOBean ioBean=new IOBean(9,10);
IOBean ioBean1=null;
try {
ioBean1=ioBean.clone();
ioBean1.setStart(100);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
System.out.println(ioBean); //结果9,10
System.out.println(ioBean1); //结果100,10
}
public static class IOBean implements Cloneable{
double start,end;
public IOBean (double start,double end){
this.start=start;
this.end=end;
}
@Override
protected IOBean clone() throws CloneNotSupportedException {
return (IOBean) super.clone();
}
浅拷贝
如上,这样的clone方式属于浅拷贝,Object.clone()默认都是浅拷贝。也就是拷贝的对象里面引用的对象没有真正的拷贝,只是复制了引用。
深拷贝
在浅拷贝的基础上,对象引用的对象也得去实现Cloneable接口,和clone方法。
- 6.lambda表达式
lambda表达式是一种简洁的语法定义代码块。 所以lambda表达式就是一个代码块,以及必须传入代码的变量规范。
lambda表达式:参数,箭头(->)以及一个表达式。就算没有参数也得要一个空()->{};
(String a, String b) -> a.compareTo(b)
如果可以推导出参数的类型,那么可以省略不写 (小括号()也可以省略)
(a,b) -> a.compareTo(b)
无须指定lambda表达式返回值类型。因为返回类型会从上下文推导得出。如上面的会返回int。
- 函数式接口
只有一个抽象方法的接口,可以用lambda表达式来调用,这样的接口称为函数式接口
Arrays.sort(arr, (a, b) -> a.compareTo(b));
ArrayList类有一个方法removeIf 可以接收lambda表达式。
list.removeIf(b->b==null) //删除列表中所有null
-
方法引用
如果要引用类中的方法 可以用::
Arrays.sort(str,String :: compareToIgnoreCase)//不区分大小写排序str;
aaa.stream().mapToDouble(Temporary :: getTimeLength).sum(); -
构造器引用
如果要调用构造器,那么方法名叫 new
aaa.stream().map(Temporary :: new).collect(Collectors.toList()); -
变量作用域
lambda表达式中捕获的变量必须实际上是事实最终变量(final)
在lambda表达式中访问外部方法或者外部变量,得那个变量不会变(事实最终变量)。
b在里面,外面 -1都是不可以的。
lambda表达式中用this,这个this是创建lambda表达式的方法的this参数
this是ro的this,不是ActionListener的this
-
处理lambda表达式
使用lambda表达式的重点是要延迟处理。
lambda表达式使用场景
● 在一个单独线程运行的代码,线程中
● 多次运行的代码,循环中
● 算法中(排序)
● 监听中(数据到达,按钮点击)
IOBean:根据start排序,如果相同就根据end排序
Arrays.sort(ioBeans, Comparator.comparingDouble(IOBean::getStart).thenComparingDouble(IOBean::getEnd));
排序可以直接用Comparator.comparing的lambda表达式实现
- 7.内部类
内部类就是定义在类里面的类
好处:
●同包中的其他类不能调用内部类
●内部类可以调用母类的数据,包括私有的
(内部类中调用母类(创建他的类)的数据的时候,编译器会自动添加上一个母类的引用-xxx.this)
局部内部类
声明在方法中的内部类,除了该方法,谁也不能访问,甚至不能有(Public或者private)
局部内部类调用方法里的局部变量的时候必须是 事实最终变量(final),跟lambda表达式调用外面的变量一样
匿名内部类
当内部类继承了类或者实现了接口的时候,可以不写内部类的名,用new + 接口名,这就叫匿名内部类,大量的接口回调其实都是匿名内部类
new 实现接口/ 父类构造器(实参列表)()
{
//匿名内部类类体部分
}
1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
2、匿名内部类中是不能定义构造函数的。
3、匿名内部类中不能存在任何的静态成员变量和静态方法。
4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
匿名内部类例子:
各种接口回调,没有用上面 implement 实现,而是直接new出来的,其实都是匿名内部类。
匿名内部类可以用lambda表达式来实现(监听,回调等)
静态内部类
如果想要内部类不能访问其母类(不生成它的外围类的引用),那么就声明成静态内部类
类不能被声明为静态,除了静态内部类
静态内部类可以用静态方法,和静态变量,别的内部类不能有
接口中声明,默认就是静态内部类
- 8.代理
代理可以在运行时创建实现了一组给定接口的新类
代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活
中常见的中介。
目的:(1)通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;
(2)通过代理对象对访问进行控制;
代理模式有三种角色:
抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个接口
真实角色:需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业
务逻辑在此。
代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附
加自己的操作。将统一的流程控制都放到代理角色中处理!
静态代理
写死的代理。
动态代理
动态代理的实质跟静态代理一样的,最重要的是代理类是通过Proxy.newProxyInstance()来创建的。
- 9.异常
异常对象都是由Throwable类继承而来。
异常分为: ①检查型异常 ②非检查型异常 见上图
检查型异常:编译器会自动检查你有没有写异常处理器。
比如随便读取一个文件
声明异常
声明异常:方法名后面+throws+异常名,见下面
得用throws FileNotFoundException声明异常或者try{}catch()捕获异常
如果有多个检查型异常得全部列出来,然后用“,”隔开。throws FileNotFoundException,EOFException。
但是非检查型异常不需要声明(Error或RuntimeException) 要么系统异常,要么自己代码写错了,不需要声明。
抛出异常
抛出异常:throw+new+异常名,见下面
有合适的异常就直接抛出,系统定义的异常没有合适的,就自定义异常。
自定义异常
在系统异常没有符合要求的时候,自定义异常。继承于Exception或者他的子类。
这就是上面图登录失败的类。
捕获异常
如果发生了异常,但是没任何地方捕获,那么程序就会终止。
堆栈轨迹
当java程序因为一个微博或的异常而终止的时候,就会显示堆栈轨迹,也就是常说的 错误日志
可以使用throwable.printStackTrace(new PrintWriter(stringWriter))来访问堆栈轨迹的文本描述信息。
● Throwable(Throwable cause)
● Throwable(String msg,Throwable cause)
Exception/RuntimeException等
构造一个Throwable对象
● Throwable initCause(Throwable cause)
设置cause原因
● Throwable getCause()
获得Throwable 异常原因
● getStackTraceElement getStackTrace()
获得堆栈轨迹
异常使用技巧
1.异常不能代替简单测试
如
if(!s.empty()) s.run();
与
try{
s.run();
}catch(EmptyStackException e){
}
虽然上下都能实现效果,如果为空就不run(),但是效率上天差地别
2.不要过分细化异常
别一行代码就try,一行代码就try,把代码块放一起,一起做处理
3.充分利用异常层次结构
具体异常抛出具体的,不要只抛出大类:RuntimeException
4.不要压制异常
有异常就去处理,或者抛出来
5.早抛出,晚捕获
- 10.断言-assert
断言机制允许在测试期间向代码插入一些检查,而在正式代码中自动删除这些代码检查。
断言x大于0 assert x>=0
断言x大于0,并返回x assert x>=0:x
启用和禁用断言
默认是禁用的,这些都是类加载器里的功能
启用: -ea java -ea:MyClass -ea:com.my.liy Myapp
禁用: -da java -da:MyClass -da:com.my.liy Myapp
- 11.日志
优点:
● 可以容易打开或者关闭某个级别下(全部)的日志
● 日志代码开销很小
● 日志记录可以采用不同方法格式化
● 日志记录器,处理器可以对记录进行过滤
● 可以启用多个日志记录器
● 日志系统的配置由配置文件控制
基本日志
全局日志记录器生成日志
Logger.getGlobal().info(“aaa”);
取消所有日志
Logger.getGlobal().setLevel(Level.OFF);
Global(翻译)整体
高级日志
创建/获取自定义日志记录器(getLogger):
private static final Logger myLogger = Logger.getLogger(“com.my.myapp”);
static:如果不声明成static,如果不调用可能会被垃圾回收
日志记录器也有层次结构,而且子类会继承和使用父类的日志级别。一共7个级别
SERVERE-严重
WARNING-警告
INFO-信息
CONFIG-配置
FINE
FINER
FINEST
默认记录前3个级别。也可以自己设置显示哪个级别setLevel(),或者全开Level.ALL或者全关Level.OFF
打印一条日志,Logger.log(等级,内容)
Logger.getLogger("com.test.proxy.dynamic_proxy").log(Level.SEVERE,"99999999999999999");
- 12.泛型程序设计
我自己文章泛型的补充: juejin.cn/post/695096…
在有泛型之前是通过继承Object来实现的(如ArrayList里维护的Object[]),这样代码非常繁琐,而且有两个问题:这也恰好是泛型的优点:让程序易读懂,更安全
- 取出值时,必须强转
在泛型之前,ArrayList里只维护了一个Object[]的引用
List list=new List(); List不是泛型,就是一个Object[],而List<String>这样就是泛型
String a=(String)list.get(0);
- 没有错误检查,什么类型都能添加,这样强转的时候就容易报错
List list=new List();
list.add(0);
list.add("111");
泛型方法
泛型方法可以定义在普通类中,也可以定义在泛型类中。
泛型只能用在对象上。如果list.add(1);这样编译器会自动将 int->转换成Integer,这个过程叫做自动装箱,get取出来叫自动拆箱
虚拟机中的泛型
虚拟机中没有泛型类型对象---所有对象都是普通类。
类型擦除
无论何时定义一个泛型类型,都会自动提供一个相应的 原始类型 。这个原始类型的名字就是去掉类型后的泛型类型名(即 List<Apple> a; a.getClass().getName();=java.util.ArrayList)。类型变量会被擦除,并替换为其限定类型(无限定类型则替换成Object)
Pair<T> 类型擦除以后所有的T都换成Object
Pair<T extends Comparable> 类型擦除以后所有的T都换成Comparable (有多个变成第一个)
擦除运行完事以后,要取到返回类型,编译器会自动插入强转代码,给我们强转成正确的类型
擦除完事以后将类型T替换成其中一种类型,但是可能有多个方法(多态),如Object.toString、String.toString。为了保持多态,编译器会生成一个桥方法
●虚拟机中没有泛型,只有普通类和方法
●所有类型参数都会替换为他们的限定类型
●会合成桥方法保持多态
●为保持类型安全性,必要时会插入强转
局限:
不支持泛型数组
通配符:允许类型参数发生变化
无线定通配符:直接一个? Pair<?>
Pair<?>与 Pair的区别在于:可以用任意Object对象调用Pair类的set方法。
作用:判null
public static Boolean hasNulls(Pair<?> p){
return p.getFirst()==null;
}
类型字面量
泛型擦除带来的问题:List,List原始类型都为List,那怎么分辨,让他有不同的动作呢?
答案:构造一个匿名内部类(同Gson,
new Gson().fromJson(s, new TypeToken<BaseBean>() {}.getType());)
{}说明的是这个创建的是一个匿名内部类,没有{}就是一个对象。如果是一个对象,那么 泛型T 还是被擦除了,变成Object了。但是+了{} 就变成声明一个匿名内部类。就 T就变成一个类,擦除以后多了一个class文件,T指向这个文件。 (大概名字)XXX.BaseBean $1.class。这个$1.class就是BaseBean的一个匿名内部类。然后这个匿名内部类里面没有T泛型,就是具体的ParameteBean,不会被擦除