【Java】 入门 — —(四)

    1、方法重载:

     A、方法重载是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数的类型或参数的个数。

        B、调用重载方法时,Java编译器能通过检查调用的方法的参数类型和个数选择一个恰当的方法。

        C、方法重载通常用于创建完成一组任务相似但参数的类型或参数的个数或参数的顺序不同的方法。

          D、Java的方法重载,就是在类中可以创建多个方法,它们可以有相同的名字,但必须具有不同的参数,即或者是参数的个数不同,或者是参数的类型不同。

        E、调用方法时通过传递给它们的不同个数和类型的参数,以及传入参数的顺序来决定具体使用哪个方法。

        F、方法重载就是方法名称重复,加载参数不同。

        G、方法重载的规范:   

           * 方法名一定要相同。

              * 方法的参数表必须不同,包括参数的类型或个数,以此区分不同的方法体。

               · 如果参数个数不同,就不管它的参数类型了!
              ·  如果参数个数相同,那么参数的类型必须不同。
            * 方法的返回类型、修饰符可以相同,也可不同。
         Ps:
          1、Elixir 中也有方法重载,不过 elixir 是函数式编程,函数为第一公民,并且 elixir 只有模块没有类(应该是没有类,不太确定)。
          2、方法重载是一个很优秀的特性,因为有它,让组合查询更加容易实现。
 

  
  2、Employee 类(我会在这里,标明一些书上说的好处等):
    
inmport java.item.*;

class Employee {
    /**
      * 1、private 是把 类的属性设置为 私有的,这里不把 属性设置为 public 公有的。
      * 2、下面会说明为什么是私有的,而不设置为公有的。
      */
    private String name;
    /// salary: 薪水、工资
    private double salary;
    /// hire: 聘用
    private LocalDate hireDay;

    /// 这个就是 构造器 / 构造函数
    public Employee(String n, double s,  int year, int month, int day)
    {
       /// 这里只是给实例域 中的属性 初始化值。
       name = n;
       salary = s;
       hireDay = LocalDate.of(year, month, day);
    }

    /// 下面是通过 访问器方法 和 更改器方法
    /// 进行实例域值的 访问 或者 更改
    
    /** 
      * 1、为什么要封装 实例域的 访问器 和 更改器方法
      * 2、上面说到,我们可以把实例域中的属性,设置为 public,这样外部    就可以直接访问和修改,也没有那么复杂。
      * 3、 但是把 数据域 设置为公有的,这样做,会导致 数据域中的可以很容易被修改。造成破坏后,没办法快速排查。
      * 4、通过封装 访问器 和 更改器,只有 更改器 方法 能进行修改,这样数据出现问题后,我们可以快速排查。
      * 5、还有一个通过 更改器方法修改域值的好处,就是我们可以封装执行错误的检查;然而通过设置 公共实例域后,直接赋值域之后,再做检查,会出现代码冗余。
      */

    /// 访问器
    public String getName() 
    {
       return name;
    }
    
    /// 访问器
    public double getSalary() 
    {
       return salary;
    }

    /// 访问器
    public LocalDate getHireDay()
    {
        /// 这里破坏了封装
        /// 因为这个访问器,返回的对象 是可变的
        /// 返回可变对象时候,要进行克隆,返回克隆的数据,这样返回出去的可变对象 改变了。也不会影响到 原对象,这样做,更改器 存在的必要。
        return hireDay;
    }

    /// 更改器
    /**
     *  隐式参数 与 显式参数:
     *     1、对象 调用 raiseSalary 方法时候,我们通常将 byPercent 叫做参数。 
     *     2、我们通常把 括号中的叫做 显式参数
     *     3、隐式参数,就是对象自己本身, 每一个方法中,关键字 this 表示隐式参数。
     *     4、下面的函数可以写成注释中的风格(这样做得好处是将实例域与局部变量明显区分开来): 
     */
    public void raiseSalary(double byPercent)
    {
        /// double raise = this.salary * byPercent / 100;
        /// this.salary += raise; 
        double raise = salary * byPercent / 100;
        salary += raise; 
    }
}            
     
      A、基于类的访问权限
  /**
   * 在上面,我们知道 Employee 有 name 这个私有属性,我们也对象的方法可以访问到对象的私有数据,然而下面的操作也是可以的: 
   */
  /// 先在 Employee 这里定义一个新的方法,对比两个 employee 类对象的name
   class Employee {
      ...
      public boolean equals(Employee other) 
      {
         return this.name.equals(other.name);
      }
   }

   /// 调用的时候,就可以通过下面的方法调用 
   if (harry.equals(boss)) ...
   /// **** 这就是 Java 中, 一个方法可以访问所属类的所有对象的私有数据。
   /// **** C++ 中也有这样的原则

    

    


  

  3、域:

    我在这里再重新整理一下 域。

                 A、field 就是域的意思。

                 B、下面是实例域 和 静态域 的区别 (这里我们都是把域设置为 public 来对比,更加清晰): 

实例域 与 静态域
修饰词 说明
实例域  无

 A、实例域 相对的是 实例 也就是对象。

 B、实例域要 先 使用 new 来 构造 对象后,在进行 对象.域名字 这样 访问。

C、实例域通过 类名.域名字 这样访问是错误的,会报错。

静态域 / 类域  static

 A、静态域相对的 是类。

 B、可以直接 就行 类名.域名字 这样访问。

        C、通过 final 关键字去定义的,就是 final 域,final 域 也有 实例域 和 静态域之分。

 


    

    4、静态方法:

      A、使用 static 修饰的方法。

                  B、静态方法中,不能使用 实例域 或者 非静态方法。如果使用了会报错:Cannot make a static reference to the non-static method getStaticTest() from the type

                  C、静态方法也没有 this 这个隐式参数,因为静态方法也是相对于 类的。

      D、非静态方法,可以使静态方法和静态域。                 

      E、静态方法的常见的用途,就是使用静态工厂方法来构造对象,使用工厂方法来创建对象的好处:
        · 当返回的类型与 类 的名字不一样时候,因为Java 中的构造器的名字与类名相同,使用静态工厂方法来构造。
        · 当使用构造函数时候,无法改变所构造的对象类型,而 Factory/工厂 方法 可以返回 类的子类对象。
 

    
     5、方法参数:
      · 将参数传递给方法(或函数)的一些专业术语:
 
术语  说明
 按值调用(call by value)  

A、按值调用 表示方法接收的是调用者提供的值。

B、按值调用的意思是,方法得到的是所有参数值的一个拷贝。

C、方法可以不修改 传递引用所对应的变量值。

 按引用调用(call by reference)

 A、按引用调用,表示方法接收的是调用者提供的变量地址。

B、方法可以修改 传递引用所对应的变量值。

A、按引用调用,表示方法接收的是调用者提供的变量地址。AA

       A、Java 总是按值调用。

       B、 *** Java 中 方法参数如果是对象,它也是按值调用的,但是按值调用的 拷贝,只是浅拷贝;

  所以可以改变对象的值,而导致原来的变量对象的值改变,但不能重新赋值一个新的对象去改变原来整个的变量对象。

 

 


 

    

    6、对象构造:

    A、上面已经说了方法重载,构造器也是能重载的。

        B、要完整的描述一个方法,需要指出 方法名 以及 参数类型,这叫做 方法的签名(signature)Ps: 方法返回 类型 和 参数名不属于里面。

      C、调用 重载 的方法时候,必须要用一个是对应上的,否则会报错 ---- 也就是说,参数类型 和 参数个数  参数类型对应的顺序,必须是对应上的,然后方法名当然也要一致(这有点废话的感觉)。

      D、如果我们没有显示的给出一个 构造函数,Java 会有一个默认的无参数构造函数

  (这个构造函数只有在我们没有给出时候,自动出现,要是我们打算有一个4个参数的构造器 和  一个无参数的构造器进行构造器的重载,那么无参数的构造器也要写出来)。

      E、上面说的默认域初始化,也就是默认构造器,初始化的数据,按数组初始化值的规则,也就是数值类型给 0,Boolean类型给 fals,对象给 null。

      F、调用另一个构造器:

/// 关键字 this, 引用方法的隐式参数外
/// 在构造器中,this(...) 的形式,是调用构造器中同一个类的另一个构造器。例如下面

class Employee
{ 
    /// 构造A
    public Employee(double salary)
    {
        /// 调用 构造B
        this("Employee_" + nextId, salary);
        nextId++;
    }    
    
    /// 构造B
    public Employee(String name, double s)
    {
       /// 这里要用 this.name 是因为 参数名和 实例域 名相同了。参数名会屏蔽掉 实例域名
       this.name = name;
       /// 这里不需要,因为 没有参数名和实例域名相同。
       salary = s;
    }
}

      G、初始化块:

import java.time.LocalDate;
class EmployeeApp
{
  public static void main(String[] args)
  {
    Employee a = new Employee("张三");
    Employee b = new Employee(4000);
    System.out.println(a);
    System.out.println(b);
  }
}


class Employee
{
  private static int nextId;
  /// 这个叫声明中赋值进行初始化
  private int id = assginId();
  private String name;
  private double salary;
  private LocalDate inserted_at;

  /// 这种叫做 初始化块,一个类中可以有多个。
  /// 只要构造类的对象,这些块都会被执行。
  {
    inserted_at = LocalDate.now();
  }

  /// 这个叫做构造器中 设置值 初始化
  public Employee(){
    name = "Employee_" + id;
  }

  public Employee(String aName)
  {
    this(aName, 3000.00);
  }

  public Employee(double aSalary)
  {
    /*
       不能 使用 下面注释掉的的写法。
       会报错:错误: 无法在调用超类型构造器之前引用id
       个人理解:
          1、不是最终调用的构造器,执行的时候,不会把 id 先实例化出来。导致出错。
          2、上面无参数的构造器不报错,是因为,id 已经先实例化了。
     */
    // this("Employee_" + id, aSalary);
    this();
    salary = aSalary;
  }

  public Employee(String aName, double aSalary)
  {
    name = aName;
    salary = aSalary;
  }

  
  public static int assginId()
  {
    int r = nextId;
    nextId++;
    return r;
  }

}

    H、静态的初始化块:

/// 如果对一个 静态域的 初始化比较复杂,可以使用一个静态的初始化块来进行初始化
/// 格式如下:
static
{
    Random generator = new Random();
    nextId = generator.nextInt(10);
}

/// 有些人认为 其实不加 static 都可以的。
/// 但是这样的结果是会导致以下情况的出现。
/// 例如:我们实例化两个对象出来,并且把他们的ID 打印出来会出现以下的结果。

/// 不使用 static 初始化块
/// 第一个 对象 的 ID 一定是 0,第二个对象的ID 是上一个对象初始化时候随机的值。
/// 意思就是 A 对象实例化的时候,nextId 会自动初始化为 0,然后再执行下面的代码 初始化 nextId. 假如为 3,然后 实例化 B 对象,那么 B 对象的ID 就是 3. 但是这个时候,它又执行了一次 下面的代码,导致 nextId 又变成了一个其他的随机数
{
    Random generator = new Random();
    nextId = generator.nextInt(10);
}

/// 使用 static 初始化块
/// 它只会在类引入进来的时候,我估计也是类加载到内存中的时候,执行一次; 之后哪怕实例化再多个对象,它也不会执行。
/// 而且它初始化值后,假如初始化为 2,那么实例化的对象的 ID 是从2 开始的(这里的 ID 是按前一个实例化的对象 ID 自增进行的)。
static
{
   Random generator = new Random();
   nextId = generator.nextInt(10); 
}

 

 

      

  

posted @ 2020-02-21 11:40  xk坤  阅读(282)  评论(0编辑  收藏  举报