面向接口编程

方便扩展,和良好的复用代码及解耦

将多个类的通用方法做抽象,使用继承或实现的方式进行灵活扩展使用

具体参考 https://mp.weixin.qq.com/s/Dg9LcKJBrabcnvC_6wga-A

枚举相关

枚举是为了良好的替代多类型的情况下的变量
可以严谨的类型约束变量数值不可控性,扩展也更方便

常用方法:

values()方法:返回所有枚举常量的数组集合
ordinal() 方法:返回枚举常量的序数,注意从0开始
compareTo()方法:枚举常量间的比较
name()方法:获得枚举常量的名称
valueOf() 方法:返回指定名称的枚举常量
可自定义扩充枚举:声明函数,构造来自定义枚举方便外部类调用
枚举 + 接口:符合开闭原则的情况进行不同情况的返回处理
枚举的集合类:
EnumSet是专门为盛放枚举类型所设计的 Set 类型。
EnumMap则是用来专门盛放枚举类型为key的 Map 类型。

深拷贝浅拷贝

赋值不属于拷贝,只是引用的转变
浅拷贝:复制对象中,值类型对象(八大类型加String)会被复制,对象中的对象只复制了引用
深拷贝 :浅拷贝的基础上,对象中的对象也会复制副本
实现方法是使对象继承Cloneable接口,重写clone()方法
通过反序列化的方法也能完成拷贝

序列化/反序列化

序列化:把Java对象转换为字节序列。
反序列化:把字节序列恢复为原先的Java对象。
序列化需对象实现Serializable接口,通过对象输出流ObjectOutputStream进行字节序列写入文件,反序列化使用ObjectInputStream读文件后强转
Serializable只是一个空接口,在ObjectOutputStream时检查了这个标记,否则抛出异常

serialVersionUID

用于检查本地字节序列和实际运行时对象的版本差异
serialVersionUID是序列化前后的唯一标识符
默认如果没有人为显式定义过serialVersionUID,那编译器会为它自动声明一个!

特殊情况:

凡是被static修饰的字段是不会被序列化的:因为序列化保存的是对象的状态而非类的状态
凡是被transient修饰符修饰的字段也是不会被序列化的:

序列化的受控和加强

序列化的过程是有漏洞不安全的(字节流被截取进行伪造)
序列化也相当于一种 “隐式的”对象构造 ,在反序列化时,进行受控的对象反序列化动作。
自行编写ObjectInputStream时的readObject()函数,设置反序列化验证

函数式编程 语法糖

https://mp.weixin.qq.com/s/yI82juBiirJ56BZfGJezLw

注解

标注用:在类、字段变量、方法、接口等位置设置标记,为代码生成、数据校验、资源整合等工作做铺垫。

实现三大步

第一步:定义注解:@注解名

  1. @Target({ElementType.FIELD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface Length{
  4. int min();
  5. // 允许字符串长度的最小值
  6. int max();
  7. // 允许字符串长度的最大值
  8. String errorMsg();
  9. // 自定义的错误提示信息
  10. }
  1. 注解的定义@interface
  2. 注解的成员变量只能使用基本类型、 String或者 enum枚举,比如 int可以,但 Integer这种包装类型就不行
  3. @Target、 @Retention 为 元注解,元注解就是java自带注解,可直接用于注解的定义
  4. @Target(xxx) 用来说明该自定义注解 可用区域
  • ElementType.FIELD:说明自定义的注解可以用于类的变量
  • ElementType.METHOD:说明自定义的注解可以用于类的方法
  • ElementType.TYPE:说明自定义的注解可以用于类本身接口enum类型
  • 等等… 还有很多,如果记不住,建议现用现查
  1. @Retention(xxx) 用来说明你自定义注解的生命周期,比如:
  • @Retention(RetentionPolicy.RUNTIME):注解可以一直保留到运行时,因此可以通过反射获取注解信息
  • @Retention(RetentionPolicy.CLASS):注解被编译器编译进 class文件,但运行时会忽略
  • @Retention(RetentionPolicy.SOURCE):注解仅在源文件中有效,编译时就会被忽略

    第二步:获取注解并对其进行验证

    利用反射获取运行时想获取注解所代包含的信息
    publicstaticString validate(Objectobject)throwsIllegalAccessException{
      // 首先通过反射获取object对象的类有哪些字段
      // 对本文来说就可以获取到Student类的id、name、mobile三个字段
      Field[] fields =object.getClass().getDeclaredFields();
      // for循环逐个字段校验,看哪个字段上标了注解
      for(Field field : fields ){
          // if判断:检查该字段上有没有标注了@Length注解
          if( field.isAnnotationPresent(Length.class)){
          // 通过反射获取到该字段上标注的@Length注解的详细信息
           Length length = field.getAnnotation(Length.class);
          field.setAccessible(true);// 让我们在反射时能访问到私有变量
          // 用过反射获取字段的实际值
           int value =((String)field.get(object)).length();
          // 将字段的实际值和注解上做标示的值进行比对
              if( value<length.min()|| value>length.max()){
              return length.errorMsg();
              }
          }
      }
    return null;
    }
    

    第三步:使用注解

    publicclassStudent{
    privateLong id;// 学号
    privateString name;// 姓名
    @Length(min =11, max =11, errorMsg ="电话号码的长度必须为11位")
    privateString mobile;// 手机号码(11位)
    }
    

    重写hashcode和equals

    在往线性表 (比如ArrayList) 中存入无序数字后,取出其中某一个数字时,需要进行遍历时间复杂度为O(n)

而往Hash表 (数据结构上的概念,比如HashMap) 存入无序数字,key会被hash算法随机出一个hashCode,和它的存储位置相关联
这样做的好处非常明显。比如我们要从中找其中一个数字,通过Hash函数计算 它的 的索引位置,然后直接使用索引里找到,平均复杂度为O(1)
注:当出现Hash值冲突(即通过hashCode算法计算后Hash值相同)时,Java的HashMap对象采用的是”链地址法”进行解决,在当前索引处创建了一个链表进行存放

为何要?

在hash表数据结构中使用自定义对象时,需要重写hashcode和equals方法
如果不进行重写,会调用超类Object的hashcode(根据内存地址hash)和equals(判断内存地址是否相等)方法,这样,即使两个看上去相等的对象也拥有不同的内存地址,hash出来也会是两个数据,并且equals判断时,永远不会相等(内存地址不同,String类就重写了equals)
示例

import java.util.HashMap;

class Key{
private Integer id;

public Integer getId(){
    return id;
}
public Key(Integer id){
    this.id = id;
}
//故意先注释掉equals和hashCode方法
//public boolean equals(Object o) {
// if (o == null || !(o instanceof Key))
// { return false; }
// else
// { return this.getId().equals(((Key) o).getId());}
// }

// public int hashCode()
// { return id.hashCode(); }
}

public class WithoutHashCode{
    public static void main(String[] args){
            Key k1 =newKey(1);
            Key k2 =newKey(1);
            HashMap<Key,String> hm =newHashMap<Key,String>();
            hm.put(k1,"Key with id is 1");
            System.out.println(hm.get(k2));
            //不重写hashcode  输出 null ---因为k2没有value
             System.out.println(k1==k2);
            //不重写equals    输出false  ---比较的是内存地址不同
        }
}

Object基类方法

https://mp.weixin.qq.com/s/eJy74CbzthHMgRPOA_4wEA