• 面向对象的三大特性为 封装、继承、多态

    封装及其必要性

  • 封装隐藏了内部实现细节,只暴露出接口(尽可能的少暴露)

  • 暴露细节的结果就是⽆法变化,例如为原先的属性增加限制或修改某些属性时会引起与之高耦合对象的修改
  • 高耦合使得类的改变变得困难,A类的改变往往涉及到与其耦合类群的改变,增加代码维护难度
  • 封装的好处实例

    • 如果要为原先的属性增加条件限制,或是修改某些属性,通过封装的接口访问比直接访问数据就显得很有优越性,在修改条件时只需要修改接口函数的逻辑即可
      1. // 直接访问
      2. public int age;
      3. dog.age = 1;
      4. // 接口访问,比直接访问具有优越性
      5. private int age;
      6. setAge(){
      7. // 可任意增加逻辑
      8. }
      9. dog.setAge(1)

      访问控制符

  • public 任何人都可以访问

  • protected 只有子类或者同一个包才能访问,包不仅可以作为区分命名空间的工具,更重要的功能是实现访问控制,注意包是没有嵌套关系的,所有包都是互相独立的。
  • package private(什么控制符也不写的时候)只有在同一个包才能访问
  • private 只有自己可以访问

    getter setter

  • 可以通过idea keymap设置的alt+insert快速生成

  • getter setter的名字是通过javaBean约定的
    • 非boolean public get\set + FirstName
    • boolean public isName\setName
  • java世界约定,JSON的读写只访问getter和setter来序列化对象,不直接访问成员变量

    设计模式-工厂模式

  • 优点

    1. 静态工厂方法的优点是可以通过名字反应加工过的构造方法的用途
    2. 与构造器相比,可以不返回一个新对象,必须实现其他逻辑
    3. 可以返回返回类型的子类型,这样就可以根据条件进行返回,提高构造函数的灵活性
    4. 可以依据输入参数选择创建的对象并返回,例如可以提供参数不合法时返回的非法对象
    5. 静态工厂函数返回的对象可以不存在,可以实现一些动态加载写类时还不存在的对象

      1. // 优点d
      2. public class Cat {
      3. private static final Cat INVALID_CAT = new Cat("Invalid cat", -1);
      4. private String name;
      5. private int age;
      6. private Cat(String name, int age) {
      7. this.name = name;
      8. this.age = age;
      9. }
      10. public static Cat newCat(String name, int age) {
      11. if (name == null || name.equals("") || age < 0) {
      12. return INVALID_CAT;
      13. }else{
      14. return new Cat(name, age);
      15. }
      16. }
      17. public String getName() {
      18. return name;
      19. }
      20. public int getAge() {
      21. return age;
      22. }
      23. }
      1. // 优点e
      2. return new Class.forName("Future Cat")
  • 缺点

    • 不能被子类化,不能继承
    • 不便于开发者查找
  • 应用实例

    • Boolean的装箱类型,任意次对valueOf方法的访问返回的都是创建好的同一TRUE\ FASLE对象,节省了时间以及内存开销

      1. public static final Boolean TRUE = new Boolean(true);
      2. public static final Boolean FASLE = new Boolean(false);
      3. public static Boolean valueOf(String s){
      4. return parseBoolean(s) ? TRUE : FASLE
      5. }
    • Integer的parseInt

    • collection中一些方法
  • 最佳实践

    • 应将构造函数改成private,使用静态工厂方法创建对象,实现对构造方法进行封装,提供新的api

      类的访问限定符

  • publicpackage private(包级私有,啥也不写)

  • 包级私有类不会对外界造成任何影响
  • 可以通过创建一个同名的包,结合工厂方法实现对其他包级私有类的访问(歪招,不建议生产使用),因为jvm判断包名字,不判断是外部依赖包还是内部包,jvm自定义了一些保留包,开发者无法自建,例如java.lang
  • 内部类用于创建逻辑单一,只在某类中使用的类,为了便于使用在一个类内部声明,访问范围只限定于该类,也是封装的一种形式

    1. private static class InnerClass{}

    java模块系统 Java Platform Module System

  • jdk9引入模块化系统,可以将一些包封装为对外的模块module

  • 模块系统提供了更大范围的封装
  • jdk8包管理系统的问题是 如果A包中的类要访问B包中的类,就必须让B包的该类变为public,但这暴露了B包该类的接口,非常不利于封装,所以引入了模块化系统,限定B类只能在模块内被访问

    扩展知识

    builder模式

  • 构造器参数过多,带来调用和重载构造器的复杂度无法判断参数顺序时,可使用该模式

  • builder模式提供了链式调用,参数赋值命名化等特性,通过return this实现(类比jQuery)

    1. // // builder改造前
    2. public class User {
    3. /** 用户的名 */
    4. private final String firstName;
    5. /** 用户的姓 */
    6. private final String lastName;
    7. /** 用户的电话 */
    8. private final String phoneNumber;
    9. /** 用户的地址 */
    10. private final String address;
    11. public String getFirstName() {
    12. return firstName;
    13. }
    14. public String getLastName() {
    15. return lastName;
    16. }
    17. public String getPhoneNumber() {
    18. return phoneNumber;
    19. }
    20. public String getAddress() {
    21. return address;
    22. }
    23. User(String firstName, String lastName, String phoneNumber, String address) {
    24. this.firstName = firstName;
    25. this.lastName = lastName;
    26. this.phoneNumber = phoneNumber;
    27. this.address = address;
    28. }
    29. }

    ```java // builder改造后 public class UserBuilder { // 请在这里使用builder模式建造User对象 // 所需的接口请参阅UserBuilderTest测试类

    private String firstName;

    private String lastName;

    private String phoneNumber;

    private String address;

    public UserBuilder firstName(String firstName){

    1. this.firstName = firstName;
    2. return this;

    }

    public UserBuilder lastName(String lastName){

    1. this.lastName = lastName;
    2. return this;

    }

    public UserBuilder phoneNumber(String phoneNumber){

    1. this.phoneNumber = phoneNumber;
    2. return this;

    }

    public UserBuilder address(String address){

    1. this.address = address;
    2. return this;

    }

    public User build(){

    1. return new User(firstName,lastName, phoneNumber,address);

    }

}

  1. ```java
  2. // 测试类
  3. public class UserBuilderTest {
  4. @Test
  5. public void canUseBuilderToCreateObjects() {
  6. User user =
  7. new UserBuilder()
  8. .firstName("德华")
  9. .lastName("刘")
  10. .phoneNumber("我也不知道")
  11. .address("可能在地球上吧")
  12. .build();
  13. Assertions.assertEquals("德华", user.getFirstName());
  14. Assertions.assertEquals("刘", user.getLastName());
  15. Assertions.assertEquals("我也不知道", user.getPhoneNumber());
  16. Assertions.assertEquals("可能在地球上吧", user.getAddress());
  17. }
  18. }

setter的链式调用

JSON序列化\反序列化与JavaBean约定

  • getter setter的名字是通过javaBean约定的
    • 非boolean public get\set + FirstName
    • boolean public isName\setName
  • java世界约定,JSON的读写只访问getter和setter来序列化对象,不直接访问成员变量