面向对象(上)

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2022-06-25

类的基本用法

class Person {
  String name;

  int age = 0;

  boolean isMale;

  public void eat() {
    System.out.println("吃饭");
  }
}

public class PersonTest {
  public static void main(String[] args) {
    // 创建Person类的实例
    Person p1 = new Person();
    p1.name = "Anna";
    p1.age = 18;
    p1.isMale = false;
    p1.eat(); // 吃饭
    System.out.println(p1.name); // "Anna"
  }
}

方法

方法的重载

在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。跟方法的修饰符、返回值类型、形参变量名、方法体都没有关系。

public class OverLoadTest {

  public void getSum(int i, int j) {
    System.out.println(i + j);
  }

  public void getSum(double i, double j) {
    System.out.println(i + j);
  }

  // 下面两个也算重载
  public void getSum(String s, int i) {}

  public void getSum(int i, String s) {}
  
  // error 不算重载 返回值不影响
  public String getSum(int i, String s) {}

  // error 不算重载 权限不影响
  private void getSum(int i, String s) {}
}

当重载存在时,在通过对象调用方法时,编译的时候会通过方法名和和参数列表来确定指定的方法。

方法的参数

可变个数形参的方法,类似于JavaScript的剩余参数,也只能是参数列表的最后一个参数,也只能存在一个可变形参。

public class OverLoadTest {
  public static void main(String[] args) {
    OverLoadTest o = new OverLoadTest();
    o.show(12);
    o.show("hello");
    o.show("hello", "world");
    o.show(); // 不传参也能匹配到使用了剩余参数定义的函数
  }

  public void show(int i) {
    System.out.println("show int " + i);
  }

  public void show(String s) {
    System.out.println("show String " + s);
  }

  public void show(String ... strs) {
    if (strs.length > 0) {
      for (int i = 0; i < strs.length; i++) {
        System.out.println("show String ... strs " + strs[i]);
      }
    } else {
      System.out.println("show String ... strs  参数列表为空");
    }
  }
}

// show int 12
// show String hello
// show strs hello
// show strs world
// show strs 参数列表为空

可变个数形参的方法与本类中方法名相同,形参不同的方法之间也构成重载,但是需要注意的是,public void show(String ... strs)public void show(String[] strs)是冲突的,不能共存,因为它们的形参列表是“一样”的。

方法形参值的传递机制

关于变量的赋值是a = b;

  • 如果变量是基本数据类型,此时赋值的是变量所保存的数据值。
  • 如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值。

形参是指方法定义时,声明的小括号内的参数;实参是方法调用时实际传递给形参的数据(可能是值,也可能是指针)

在方法调用传入实参时,可以这样理解,此时在方法中根据形参列表,声明了多个局部变量,并将实参的值传递给了这些变量。

public class ValueTransfer {

  public static void main(String[] args) {
    ValueTransfer v = new ValueTransfer();

    int a = 10;
    int b = 20;
    v.swap(a, b);
    System.out.println("main方法内访问main方法内的局部变量");
    System.out.println("a => " + a + ", b => " + b);
    // a => 10, b => + 20
  }


  public void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
    System.out.println("swap方法内访问的形参,是swap方法内的局部变量");
    System.out.println("a => " + a + ", b => " + b);
    // a => 20, b => + 10
  }
}

a => 10, b => + 20

如上述例子,是一个值传递,所以呢,在swap方法中交换的只是 swap 被调用时内部的局部变量。

总结:如果传递的数据是基本数据类型,此时实参赋值给形参的是实参真实存储的数据值。

public class ValueTransfer2 {
  public static void main(String[] args) {
    ValueTransfer2 v = new ValueTransfer2();

    Data data = new Data();
    data.m = 10;
    data.n = 20;
    v.swap(data);
    System.out.println("m => " + data.m + ", n => " + data.n);
    // m => 20, n => 10
  }

  public void swap(Data d) {
    int temp = d.m;
    d.m = d.n;
    d.n = temp;
    System.out.println("m => " + d.m + ", n => " + d.n);
    // m => 20, n => 10
  }
}

class Data {
  int m;
  int n;
}

上述例子,传递给swap方法是Data data这个对象,所有在调用方法时,在swap也创建了一个局部变量,可以理解为是一个赋值Data d = data;,此时的这个 d 保存的指针(引用地址),之后在方法体中去修改d.md.n的时候,是修改的Data data = new Data(); 这个对象在内存中保存的相应属性的数据,所以可以影响到main方法中的Data data(因为这个变量也是一个指针,它们两个指向的是同一个内存地址)

总结:如果传递的数据是引用数据类型,此时实参赋值给形参的是实参存储数据的地址值。

递归

递归方法是指一个方法体内调用它自身,方法递归包含了一种隐式的循环,它会重复执行某段代码,所有递归方法的方法体中关于结束判断要合理设置,避免死循环。

封装性

隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏,该暴露的暴露出来,这就是封装性的设计思想,追求高内聚低耦合。

高内聚:类的内部数据操作细节自己完成,不允许外部干涉;

低耦合:仅对外暴露少量的方法用于使用。

public class AnimalTest {
  public static void main(String[] args) {
    Animal a = new Animal();
    a.name = "wangcai";
    a.setAge(3);
    //a.age = -3; // 如果Animal类的age属性没有设置private,则会导致用户可以通过 a.age 直接修改属性,并设置一个不合理的值。
    a.show();
    a.eat();
  }
}

class Animal {
  String name;
  private  int age;

  public void setAge(int age) {
    if (age > 0) {
      this.age = age;
    } else {
      this.age = 0;
    }
  }

  public int getAge() {
    return age;
  }


  public void eat() {
    System.out.println("吃");
  }

  public void show() {
    System.out.println("name:" + name + ",age:" + age);
  }
}

当我们创建一个类的对象以后,我们可以通过 对象.属性 的方式,对对象的属性进行赋值。这里的赋值操作要受属性的数据类型和存储范文的制约,除此之外,没有其他的约束条件。但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件,这个条件就不能在属性声明时体现,我们只能通过方法进行限制条件的增加,比如上例子中的 setAge。同时,我们在提供set方法的时候,还需要避免用户再使用 对象.属性 的方法去给属性赋值,则需要将属性声明为私有的(private)。

当我们设置属性为私有的时候,如果只提供set方法,那么使用者将无法读取该属性的值,所以此时应该提供一个get方法给用户,让其可以通过该方法访问到对应的属性,比如上例中的 getAge

此时,针对于属性就体现了封装性,体现在我们将类的属性私有化,同时提供公共的set和get方法出去用于设置和获取。

权限修饰符

权限从小到大包含,private、缺省(不修饰)、protectedpublic

这些权限修饰符用来限制对象对该类成员的访问权限。

修饰符 类内部 同一个包 不同包的子类 同一个工程
private yes
缺省 yes yes
protected yes yes yes
public yes yes yes yes

需要注意的有:

  • 四种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类。

  • 对于class的权限只可以用public 或者 缺省。

    • public 可以在任意地方被访问
    • 缺省修饰的类只可以被同一个包内部的类访问。

构造器

构造器的作用:

  • 创建对象: new Person() 的结构就是 new + 构造器,当new Person()的时候,实际执行的是Person的构造器(构造方法)。

  • 初始化对象的信息

需要注意的是:

  • 如果没有显示的定义类的构造器的话,则系统默认为提供一个空参构造器,默认提供的空参构造器的权限与当前类的权限一致。

  • 定义构造器的格式: 权限修饰符 类名(形参列表){}

  • 一个类中可以存在多个构造器,就是对构造器的重载。

  • 一旦我们显示定义了类的构造器之后,系统就不再提供默认的空参构造器。

class Person {
  String name;

  int age = 0;

  // 构造器 如果没有显示的写 就有一个空参的在这 是系统提供的
  public Person() {
    System.out.println("构造器被执行了");
  }

  public Person(String _name) {
    name = _name;
    System.out.println("带有name参数构造器被执行了");
  }
}

public class PersonTest {
  public static void main(String[] args) {
    // 创建Person类的实例
    Person p1 = new Person(); // 输出 "构造器被执行了"

    Person p2 = new Person("jack"); // 输出 "带有name参数构造器被执行了"
    System.out.println(p2.name); // 输出 "jack"
  }
}

属性赋值的先后顺序

属性赋值的先后顺序如下:

  • 1、默认初始化对应类型的值,比如上例中第2行的 String name,就会对name进行初始化为null,因为是引用类型。

  • 2、显示初始化,比如上例中的第4行 int age = 0,就是显示初始化,如果之后没有在构造器中进行赋值通过对象.属性的方法去改变的话,之后访问就是一直是0。

  • 3、构造器中赋值,比如上例中的第22行 Person p2 = new Person("jack"),就是在构造器中给name进行了赋值。

  • 4、通过 对象.方法 或者 对象.属性 的方式去赋值,可以多次使用,上面三个只能在“初始化过程”中使用一次。

JavaBean

JavaBean 是一种Java语言写成的可重用组件。

所有的JavaBean,是指符合如下标准的Java类:

  • 类是公共的
  • 有一个无参的公共的构造器
  • 有属性,且有这些属性对应的get、set方法

UML类图

使用一种图形的方式表示整个类的结构。

其中,需要注意的是

  • -表示private
  • +表示public
  • #表示protected
  • ~表示default
  • _表示static
  • 斜体表示抽象
  • 下划线表示构造器

this的使用

this修饰属性和方法:

  • this理解为当前对象或当前正在创建的对象(在构造器中)

  • 在类的方法中,我们可以使用 this.属性this.方法 的方式,调用当前对象属性或方法,但是,通常情况下,我们选择省略 this。特殊情况下,如果方法的形参和类的属性名相同,我们必须显示使用 this.变量 的方法,表示次变量是属性,而非形参。

public class PersonTest1 {
  public static void main(String[] args) {
    Person1 p = new Person1("jack", 17);
    p.setAge(18); // p调用setAge时,this指向p
    System.out.println(p.getAge());
    p.getup();
  }
}

class Person1 {
  private String name;
  private int age;

  public void setName(String name) {
    // 属性名和参数形参相同 所以要显示使用this.变量
    this.name = name;
  }

  public Person1(String name, int age) {
    this.name = name; // 在构造器中,this指向正在创建的那个对象
    this.age = age;
  }

  public String getName() {
    return name;
  }

  public void setAge(int a) {
    // 属性名和参数形参不同时,可以省略this
    age = a;
  }

  public int getAge() {
    return age;
  }

  public void eat() {
    System.out.println("吃饭");
  }

  public void getup() {
    System.out.println("起床");
    // 通常调用时,省略this
    eat();
    //this.eat(); // 与上效果一致
  }
}

this修饰构造器

在一个构造器中调用另一个构造器。

public class PersonTest2 {
  public static void main(String[] args) {
    Person2 p = new Person2("jack", 18); // 吃饭
    System.out.println(p.name); // jack
    System.out.println(p.age); // 18
  }
}

class Person2 {
  String name;
  int age;

  public Person2() {
    this.eat();
  }

  public Person2(String name) {
    this(); // 调用 Person2()
    this.name = name;
  }

  public Person2(String name, int age) { // 业务处调用,先执行这个
    this(name); // 调用 Person2(String name)
    this.age = age;
    // 最终也能成功调用eat,赋值name属性
  }

  public void eat() {
    System.out.println("吃饭");
  }
}

我们在类的构造器中,可以显示的使用 this(形参列表) 的方式,调用本类中指定的其他构造器,但是不能使用这种方法调用“自己”(当前构造器)。

同时,使用this(形参列表)的方法调用其他构造器,必须声明在当前构造器的首行,所以每个构造器内部,最多只能调用一个其他的构造器。

构造器的互相调用,需要注意不能循环调用,每个构造器在一次构造中都只能最多调用一次,一次构造调用了多个构造器也只是创建了一个对象。

package关键字

1、为了更好的实现项目中类的管理,提供包(package)的概念。

2、使用package声明类或接口所属的包,声明在源文件的首行。

3、包,属于标识符,需要遵循标识符的命名规则、规范(小写、见名知意)。

4、每 “.” 一次,就代表一层文件目录。

5、同一个包下,不能命令同名的接口、类,不同的包下可以。

6、JDK中主要的包介绍

  • java.lang - 包含一些java语言的核心类,如String、Math、Integer、System和Thread,提供常用功能。

  • java.net - 包含执行与网络相关的操作的类和接口。

  • java.io - 包含能提供多种输入/输出功能的类。

  • java.util - 包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。

  • java.text - 包含了一些java格式化相关的类

  • java.sql - 包含了java进行JDBC数据库编程的相关类/接口。

  • java.awt - 包含了构成抽象窗口工具集(abstract/window/toolkits)的多个类,这些被用来构建和管理应用程序的图形用户界面(GUI)。

import关键字

1、在源文件中显示的使用import结构导入指定包下的类、接口。

2、声明在包的声明和类的声明之间。

3、可以使用*的方式,导入某个包下的所有结构,import java.util.*

4、如果使用的类或接口是在java.lang定义的,则可以省略import结构。

5、如果在源文件,使用了不同包下的同名的类,则必须至少有一个采用全类名的方式显示。

6、如果使用*方式可导入某包下面的所有接口,但是如果使用该包下子包的结构,则仍需显示导入。

7、import static:导入指定类或接口中的静态结构(引入的是属性或方法,而import引入的类或者接口)

客户信息管理系统demo代码

Customer.java

public class Customer {
  private String name;
  private char gender;
  private int age;
  private  String phone;
  private String email;

  public Customer() {
  }

  public Customer(String name, char gender, int age, String phone, String email) {
    this.name = name;
    this.gender = gender;
    this.age = age;
    this.phone = phone;
    this.email = email;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public char getGender() {
    return gender;
  }

  public void setGender(char gender) {
    this.gender = gender;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }

  public String getPhone() {
    return phone;
  }

  public void setPhone(String phone) {
    this.phone = phone;
  }

  public String getEmail() {
    return email;
  }

  public void setEmail(String email) {
    this.email = email;
  }
}

CustomerList.java

public class CustomerList {
  private Customer[] customers;
  private int total = 0;

  /**
   * 用来初始化customers数组的构造器
   * @param totalCustomer 指定数组的长度
   */
  public CustomerList(int totalCustomer) {
    customers = new Customer[totalCustomer];
  }

  /**
   * 将指定客户添加到数组中
   * @param customer
   * @return
   */
  public boolean addCustomer(Customer customer) {
    if (total >= customers.length) {
      return false;
    }
    customers[total] = customer;
    total++;
    return true;
  }

  /**
   * 修改指定索引位置的客户信息
   * @param index
   * @param customer
   * @return
   */
  public boolean replaceCustomer(int index, Customer customer) {
    if (index >= total || index < 0) {
      return false;
    }
    customers[index] = customer;
    return true;
  }

  /**
   * 删除指定位置的客户
   * @param index
   * @return
   */
  public boolean deleteCustomer(int index) {
    if (index >= total || index < 0) {
      return false;
    }
    for (int i = index; i < total - 1; i++) {
      customers[i] = customers[i+1];
    }
    customers[total-1] = null;
    total--;
    return true;
  }

  /**
   * 获取所有的客户信息
   * @return
   */
  public Customer[] getAllCustomers() {
    Customer[] custs = new Customer[total];
    for (int i = 0; i < total; i++) {
      custs[i] = customers[i];
    }
    return custs;
  }

  public Customer getCustomer(int index) {
    if (index >= total || index < 0) {
      return null;
    }
    return customers[index];
  }

  public int getTotal() {
    return total;
  }
}

CMUtility.java

package com.cz.p2.util;

import java.util.*;

public class CMUtility {

  private static Scanner scanner = new Scanner(System.in);

  /** 读取键盘输入 如果用户键入1-5中的任意字符,则方法返回用户键入的字符。*/
  public static char readMenuSelection() {
    char c;
    for(;;) {
      c = readChar();
      String[] chars = new String[]{"1", "2", "3", "4", "5"};
      if (Arrays.asList(chars).contains(String.valueOf(c))) {
        break;
      } else {
        System.out.print("选择错误,请重新输入:");
      }
    }
    return c;
  }

  /** 从键盘读取字符,并将其作为方法的返回值 如果用户直接回车,则返回默认值 */
  public static char readChar(char ...args) {
    boolean hasDefaultValue = args != null;
    String str = readKeyBoard(1, hasDefaultValue);
    return hasDefaultValue && str.length() == 0 ? args[0] : str.charAt(0);
  }

  /** 从键盘读取一个长度不超过2位的整数并返回 如果用户直接回车,则返回默认值 默认值通过参数传入 */
  public static int readInt(int ...args) {
    boolean hasDefaultValue = args != null;
    int n;
    for(;;) {
      String str = readKeyBoard(2, hasDefaultValue);
      if (str.equals("") && hasDefaultValue) {
        return args[0];
      }

      try {
        n = Integer.parseInt(str);
        break;
      } catch (NumberFormatException e) {
        System.out.print("数字输入错误,请重新输入:");
      }
    }
    return n;
  }

  /** 从键盘读取不超过limit的字符串,并将其作为方法的返回值 如果用户直接回车,则返回默认值 */
  public static String readString(int limit, String ...args) {
    boolean hasDefaultValue = args != null;
    String str = readKeyBoard(limit, hasDefaultValue);
    return hasDefaultValue && str.length() == 0 ? args[0] : str;
  }

  /** 用于选择,从键盘读取Y/N, 并将其返回 */
  public static char readConfirmSelect() {
    String select =  readString(1, "Y").toUpperCase();
    char c = select.charAt(0);
    if (c == 'Y' || c == 'N') {
      return  c;
    } else {
      System.out.print("输入错误,请重新输入(y/n):");
      return readConfirmSelect();
    }
  }

  /**
   * 读取键盘输入
   *
   * @param limit 字符的限制长度
   * @param blankReturn 用户回车时是否直接返回
   * @return 返回读取的数据
   */
  private static String readKeyBoard(int limit, boolean blankReturn) {
    String line = "";
    while (scanner.hasNextLine()) {
      line = scanner.nextLine();
      int len = line.length();
      if (len == 0) {
        if (blankReturn) {
          return  line;
        } else {
          continue;
        }
      } else if (len < 1 || len > limit) {
        System.out.print("输入长度(不大于" + limit + ")错误,请重新输入");
      }
      break;
    }
    return line;
  }
}

CustomerView.java

public class CustomerView {
  private CustomerList customerList = new CustomerList(10);

  public static void main(String[] args) {
    CustomerView view = new CustomerView();
    view.enterMainMenu();
  }

  /**
   * 显示界面的方法
   */
  public void enterMainMenu() {
    boolean isFlag = true;

    while(isFlag) {
      System.out.println("\n------------客户信息管理软件------------\n");
      System.out.println("             1 添加客户");
      System.out.println("             2 修改客户");
      System.out.println("             3 删除客户");
      System.out.println("             4 客户列表");
      System.out.println("             5 退出\n");
      System.out.print("            请选择(1-5):");

      char menu = CMUtility.readMenuSelection();

      switch (menu) {
        case '1':
          addNewCustomer();
          break;
        case '2':
          modifyCustomer();
          break;
        case '3':
          deleteCustomer();
          break;
        case '4':
          listAllCustomers();
          break;
        case '5':
          System.out.print("确认是否退出(Y/N):");
          char isExit = CMUtility.readConfirmSelect();
          if (isExit == 'Y') {
            isFlag = false;
          }
      }
    }
  }

  private void addNewCustomer() {
    System.out.println("\n------------添 加 客 户------------\n");
    System.out.print("姓名: ");
    String name = CMUtility.readString(10);

    System.out.print("性别: ");
    char gender = CMUtility.readChar();

    System.out.print("年龄: ");
    int age = CMUtility.readInt();

    System.out.print("电话: ");
    String phone = CMUtility.readString(11);

    System.out.print("邮箱: ");
    String email = CMUtility.readString(20);

    Customer c = new Customer(name, gender, age, phone, email);
    boolean isSuccess = customerList.addCustomer(c);
    if (isSuccess) {
      System.out.println("------------添 加 成 功------------");
    } else {
      System.out.println("------------添 加 失 败------------");
    }
  }

  private void modifyCustomer() {
    Customer c;
    int number;
    System.out.println("\n------------修 改 客 户------------\n");
    for(;;) {
      System.out.print("请选择待修改客户编号(-1退出):");
      number = CMUtility.readInt();
      if (number == -1) {
        return;
      }
      c = customerList.getCustomer(number);
      if (c == null) {
        System.out.println("无法找到指定的客户!");
      } else {
        break;
      }
    }
    // 找到了
    System.out.print("姓名(" + c.getName() + "):");
    String name = CMUtility.readString(10, c.getName());

    System.out.print("性别(" + c.getGender() + "):");
    char sex = CMUtility.readChar(c.getGender());

    System.out.print("年龄(" + c.getAge() + "):");
    int age = CMUtility.readInt(c.getAge());

    System.out.print("电话(" + c.getPhone() + "):");
    String phone = CMUtility.readString(11, c.getPhone());

    System.out.print("邮箱(" + c.getEmail() + "):");
    String email = CMUtility.readString(20, c.getEmail());

    Customer newCustomer = new Customer(name, sex, age, phone, email);
    boolean isSuccess = customerList.replaceCustomer(number, newCustomer);
    if (isSuccess) {
      System.out.println("------------修 改 成 功------------");
    } else {
      System.out.println("------------修 改 失 败------------");
    }
  }

  private void deleteCustomer() {
    Customer c;
    int number;

    System.out.println("\n------------删 除 客 户------------\n");
    for(;;) {
      System.out.print("请选择待删除客户编号(-1退出):");
      number = CMUtility.readInt();
      if (number == -1) {
        return;
      }
      c = customerList.getCustomer(number);
      if (c == null) {
        System.out.println("无法找到指定的客户!");
      } else {
        break;
      }
    }

    System.out.print("确认是否删除(Y/N):");
    char isDelete = CMUtility.readConfirmSelect();
    if (isDelete == 'Y') {
      boolean isSuccess = customerList.deleteCustomer(number);
      if (isSuccess) {
        System.out.println("------------删 除 成 功------------");
      } else {
        System.out.println("------------删 除 失 败------------");
      }
    }
  }

  private void listAllCustomers() {
    System.out.println("\n------------客 户 列 表------------\n");
    int total = customerList.getTotal();
    if (total == 0) {
      System.out.println("没有客户记录!");
    } else {
      System.out.println("编号\t\t\t姓名\t\t\t性别\t\t\t年龄\t\t\t电话\t\t\t\t邮箱");
      Customer[] customers = customerList.getAllCustomers();
      for (int i = 0; i < total; i++) {
        Customer c = customers[i];
        System.out.println(i + "\t\t\t" + c.getName() + "\t\t\t" + c.getGender() + "\t\t\t"
        + c.getAge() + "\t\t\t" + c.getPhone() + "\t\t\t" + c.getEmail());
      }
    }
    System.out.println("\n------------客 户 列 表------------\n");
  }
}