Java编程思想笔记

第5章 初始化#

5.1 用构造器确保初始化#

Java采用与C++相同的解决方案:构造器采用与类相同的名字。

class Rock {
  Rock() { // This is the constructor
    System.out.print("Rock ");
  }
}

public class SimpleConstructor {
  public static void main(String[] args) {
    for(int i = 0; i < 10; i++)
      new Rock();
  }
} /* Output:
Rock Rock Rock Rock Rock Rock Rock Rock Rock Rock

注意,由于构造器的名称必须与类名相同,所以“每个方法首字母小写的风格并不适用于构造器”。不接受任何参数的构造器叫做无参构造器。

5.2 方法重载#

为了方法名相同而参数不同的构造器同时存在,必须用到方法重载。

import static net.mindview.util.Print.*;

class Tree {
  int height;
  Tree() {
    print("Planting a seedling");
    height = 0;
  }
  Tree(int initialHeight) {
    height = initialHeight;
    print("Creating new Tree that is " +
      height + " feet tall");
  }	
  void info() {
    print("Tree is " + height + " feet tall");
  }
  void info(String s) {
    print(s + ": Tree is " + height + " feet tall");
  }
}

public class Overloading {
  public static void main(String[] args) {
    for(int i = 0; i < 5; i++) {
      Tree t = new Tree(i);
      t.info();
      t.info("overloaded method");
    }
    // Overloaded constructor:
    new Tree();
  }	
} /* Output:
Creating new Tree that is 0 feet tall
Tree is 0 feet tall
overloaded method: Tree is 0 feet tall
Creating new Tree that is 1 feet tall
Tree is 1 feet tall
overloaded method: Tree is 1 feet tall
Creating new Tree that is 2 feet tall
Tree is 2 feet tall
overloaded method: Tree is 2 feet tall
Creating new Tree that is 3 feet tall
Tree is 3 feet tall
overloaded method: Tree is 3 feet tall
Creating new Tree that is 4 feet tall
Tree is 4 feet tall
overloaded method: Tree is 4 feet tall
Planting a seedling

5.2.1 区分重载方法#

当方法名相同时,Java通过参数类型列表来判断你指向的哪一个方法。

5.2.2 涉及基本类型的重载#

//: initialization/PrimitiveOverloading.java
// Promotion of primitives and overloading.
import static net.mindview.util.Print.*;

public class PrimitiveOverloading {
  void f1(char x) { printnb("f1(char) "); }
  void f1(byte x) { printnb("f1(byte) "); }
  void f1(short x) { printnb("f1(short) "); }
  void f1(int x) { printnb("f1(int) "); }
  void f1(long x) { printnb("f1(long) "); }
  void f1(float x) { printnb("f1(float) "); }
  void f1(double x) { printnb("f1(double) "); }

  void f2(byte x) { printnb("f2(byte) "); }
  void f2(short x) { printnb("f2(short) "); }
  void f2(int x) { printnb("f2(int) "); }
  void f2(long x) { printnb("f2(long) "); }
  void f2(float x) { printnb("f2(float) "); }
  void f2(double x) { printnb("f2(double) "); }

  void f3(short x) { printnb("f3(short) "); }
  void f3(int x) { printnb("f3(int) "); }
  void f3(long x) { printnb("f3(long) "); }
  void f3(float x) { printnb("f3(float) "); }
  void f3(double x) { printnb("f3(double) "); }

  void f4(int x) { printnb("f4(int) "); }
  void f4(long x) { printnb("f4(long) "); }
  void f4(float x) { printnb("f4(float) "); }
  void f4(double x) { printnb("f4(double) "); }

  void f5(long x) { printnb("f5(long) "); }
  void f5(float x) { printnb("f5(float) "); }
  void f5(double x) { printnb("f5(double) "); }

  void f6(float x) { printnb("f6(float) "); }
  void f6(double x) { printnb("f6(double) "); }

  void f7(double x) { printnb("f7(double) "); }

  void testConstVal() {
    printnb("5: ");
    f1(5);f2(5);f3(5);f4(5);f5(5);f6(5);f7(5); print();
  }
  void testChar() {
    char x = 'x';
    printnb("char: ");
    f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x); print();
  }
  void testByte() {
    byte x = 0;
    printnb("byte: ");
    f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x); print();
  }
  void testShort() {
    short x = 0;
    printnb("short: ");
    f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x); print();
  }
  void testInt() {
    int x = 0;
    printnb("int: ");
    f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x); print();
  }
  void testLong() {
    long x = 0;
    printnb("long: ");
    f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x); print();
  }
  void testFloat() {
    float x = 0;
    printnb("float: ");
    f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x); print();
  }
  void testDouble() {
    double x = 0;
    printnb("double: ");
    f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x); print();
  }
  public static void main(String[] args) {
    PrimitiveOverloading p =
      new PrimitiveOverloading();
    p.testConstVal();
    p.testChar();
    p.testByte();
    p.testShort();
    p.testInt();
    p.testLong();
    p.testFloat();
    p.testDouble();
  }
} /* Output:
5: f1(int) f2(int) f3(int) f4(int) f5(long) f6(float) f7(double)
char: f1(char) f2(int) f3(int) f4(int) f5(long) f6(float) f7(double)
byte: f1(byte) f2(byte) f3(short) f4(int) f5(long) f6(float) f7(double)
short: f1(short) f2(short) f3(short) f4(int) f5(long) f6(float) f7(double)
int: f1(int) f2(int) f3(int) f4(int) f5(long) f6(float) f7(double)
long: f1(long) f2(long) f3(long) f4(long) f5(long) f6(float) f7(double)
float: f1(float) f2(float) f3(float) f4(float) f5(float) f6(float) f7(double)
double: f1(double) f2(double) f3(double) f4(double) f5(double) f6(double) f7(double)
*///:~

当传入的数据类型(实际的数据类型)小于方法中声明的形式参数类型,实际数据类型就会被提升。char有所不同,如果找不到恰好接收char参数的方法,就会把char提升至int型。
类型转换顺序:

5.3 默认构造器#

如果你写的类没有构造器,则编译器会自动化帮你创建一个默认构造器。

//: initialization/DefaultConstructor.java

class Bird {}

public class DefaultConstructor {
  public static void main(String[] args) {
    Bird b = new Bird(); // Default!
  }
} 

new Bird()创建了一个新对象,并且调用了默认构造器。但是如果你已经定义了一个构造器(无论是否有参数),编译器都不会为你自动创建默认构造器。

//: initialization/NoSynthesis.java

class Bird2 {
  Bird2(int i) {}
  Bird2(double d) {}
}

public class NoSynthesis {
  public static void main(String[] args) {
    //! Bird2 b = new Bird2(); // No default
    Bird2 b2 = new Bird2(1);
    Bird2 b3 = new Bird2(1.0);
  }
} ///:~

这时候,如果你写new Bird()编译器就会报错:没有找到匹配的构造器。

5.4 this关键字#

this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。
只有当需要明确指出对当前对象的引用时,才需要使用this关键字。

//: initialization/Leaf.java
// Simple use of the "this" keyword.

public class Leaf {
  int i = 0;
  Leaf increment() {
    i++;
    return this;
  }
  void print() {
    System.out.println("i = " + i);
  }
  public static void main(String[] args) {
    Leaf x = new Leaf();
    x.increment().increment().increment().print();
  }
} /* Output:
i = 3
*///:~

this对将当前对象传递给其他方法也很有用:

//: initialization/PassingThis.java

class Person {
  public void eat(Apple apple) {
    Apple peeled = apple.getPeeled();
    System.out.println("Yummy");
  }
}

class Peeler {
  static Apple peel(Apple apple) {
    // ... remove peel
    return apple; // Peeled
  }
}

class Apple {
  Apple getPeeled() { return Peeler.peel(this); }
}

public class PassingThis {
  public static void main(String[] args) {
    new Person().eat(new Apple());
  }
} /* Output:
Yummy
*///:~

为了将自身传递给外部方法,Apple必须使用this关键字。

5.4.1 在构造器中调用构造器#

//: initialization/Flower.java
// Calling constructors with "this"
import static net.mindview.util.Print.*;

public class Flower {
  int petalCount = 0;
  String s = "initial value";
  Flower(int petals) {
    petalCount = petals;
    print("Constructor w/ int arg only, petalCount= "
      + petalCount);
  }
  Flower(String ss) {
    print("Constructor w/ String arg only, s = " + ss);
    s = ss;
  }
  Flower(String s, int petals) {
    this(petals);
//!    this(s); // Can't call two!
    this.s = s; // Another use of "this"
    print("String & int args");
  }
  Flower() {
    this("hi", 47);
    print("default constructor (no args)");
  }
  void printPetalCount() {
//! this(11); // Not inside non-constructor!
    print("petalCount = " + petalCount + " s = "+ s);
  }
  public static void main(String[] args) {
    Flower x = new Flower();
    x.printPetalCount();
  }
} /* Output:
Constructor w/ int arg only, petalCount= 47
String & int args
default constructor (no args)
petalCount = 47 s = hi
*///:~

表明:可以用this调用一个构造器,但不能调用两个,而且需要把构造器调用置于最起始处,否则编译器会报错。在参数s的名称与数据成员的名称s相同可能会产生歧义时,使用this.s代表数据成员可以解决这个问题。
printPetalCount()方法表明,除了构造器之外禁止在其他任何方法中调用构造器。

5.4.2 static的含义#

static方法就是没有this的方法。可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法,在static方法的内部不能调用非静态方法。Java中禁止使用全剧方法,但你在类中用static方法就可以访问其他static域和static方法。

第6章 访问权限控制#

Java提供访问权限修饰词,供类库开发人员向客户端程序员指明哪些是可以可用的,哪些是不可用的。访问权限控制的等级,从大到小依次为:public、protected、包访问权限(没有关键字)和private。
Java用关键字package来控制如何将构件捆绑到一个内聚的类库单元中的问题,而访问权限修饰词会因类是存在于一个相同的包,还是存在于一个单独的包而受到影响。

6.1 包:库单元#

当编写一个Java源代码文件时,此文件通常被称为编译单元。每个编译单元都必须有一个后缀名.java,而在编译单元内则可以有一个public类,该类的名称必须与文件名相同。每个编译单元只能有一个public类。如果编译单元之中还有额外的类,那么在包之外是无法看见这些类的,这是因为他们不是public类,而且他们主要用来为主public类提供支持。

6.1.1 代码组织#

类库实际上是一组类文件。其中每个文件都有一个public类,以及任意数量的非public类。因此每个文件都有一个构件。
如果使用package语句,它必须是文件中除注释以外的第一句程序代码。在文件起始处写:
package access;
就表明你在声明该编译单元是名为access的类库的一部分。任何想要使用该名称的人都必须使用前面给出的选择,指定全名或者与access结合使用的import。(注意,Java包的命名规则全部使用小写字母,包括中间的名字)
例如,假设文件的名称是Myclass.java,就意味着该文件中有且只有一个public类,该类的名称必须是Myclass

6.2 Java访问权限修饰词#

publicprotectedprivate这几个Java访问权限修饰词在使用时,是置于类中每个成员的定义之前的。

6.2.1 包访问权限#

默认访问权限没有任何关键字,但通常是指包访问权限。这就意味着当前的包中的所有其他类对那个成员都有访问权限,但对于这个包之外的所有类,这个成员却是private

6.2.2 public:接口访问权限#

使用关键字public,就意味着public之后紧跟着的成员声明自己对每个人都是可用的

6.2.3 private:你无法访问#

关键字private意思是,除了包含该成员的类之外,其他任何类都无法访问这个成员。
任何可以肯定只是该类的一个辅助方法的方法都可以指定为private,以确保你不会再包内的其他地方误用到它,于是也就防止了你会去改变或删除这个方法。

6.2.4 protect:继承访问权限#

详见的第七章

6.3 接口和实现#

详见第七章

6.4 类的访问权限#

类既不可以是private,又不可以是protect。所以对于类的访问权限,仅有两个选择:包访问权限或public
如果方法可以肯定只是该类中的一个“助手”方法,就可以把它指定为private,以确保不会在包内的其他地方误用到它,于是也就防止了你会去改变或删除这个方法。

   //: access/IceCream.java
// Demonstrates "private" keyword.

class Sundae {
  private Sundae() {}
  static Sundae makeASundae() {
    return new Sundae();
  }
}

public class IceCream {
  public static void main(String[] args) {
    //! Sundae x = new Sundae();
    Sundae x = Sundae.makeASundae();
  }
} ///:~

在上述例子中,不能通过构造器来创建Sundae对象,而必须调用makeASundae()方法来达到此目的。
此例还有另外一个效果:既然默认构造器是唯一定义的构造器,并且是private的,那么它将阻碍对此类的继承。

第7章 复用类#

7.1 组合语法#

使用组合技术,只需要将对象引用置于新类中即可。对于非基本类型的对象,必须将其引用置于新的类,但可以直接定义基本数据类型:

//: reusing/SprinklerSystem.java
// Composition for code reuse.

class WaterSource {
  private String s;
  WaterSource() {
    System.out.println("WaterSource()");
    s = "Constructed";
  }
  public String toString() { return s; }
}	

public class SprinklerSystem {
  private String valve1, valve2, valve3, valve4;
  private WaterSource source = new WaterSource();
  private int i;
  private float f;
  public String toString() {
    return
      "valve1 = " + valve1 + " " +
      "valve2 = " + valve2 + " " +
      "valve3 = " + valve3 + " " +
      "valve4 = " + valve4 + "\n" +
      "i = " + i + " " + "f = " + f + " " +
      "source = " + source;
  }	
  public static void main(String[] args) {
    SprinklerSystem sprinklers = new SprinklerSystem();
    System.out.println(sprinklers);
  }
} /* Output:
WaterSource()
valve1 = null valve2 = null valve3 = null valve4 = null
i = 0 f = 0.0 source = Constructed
*///:~

SprinklerSystem.toString()表达式中:
"source = "+ source;
编译器将会知道你洗那个想要将一个String对象("source=")同WaterSource相加。由于一个String对象只能和另一个String对象相加,因此编译器将调用toString(),把source转换成另一个String
类中域为基本类型时能够自动被初始化为零。但是对象引用会被初始化为null,而且如果你试图为它们调用任何方法,都会得到一个异常——运行时错误。方便的是,在不抛出异常的情况下仍旧可以打印一个null引用。

7.2 继承语法#

继承是所有OOP语言和Java语言中不可缺少的组成部分。当创建一个类时,总是在继承,因此,除非已明确指出要从其他类中继承,否则就是在隐式地从Java的标准根类Object进行继承。
在继承过程中,需要先声明“新类与旧类相似”。一般是类名+extends+基类名称来实现的。当这么做时,会自动得到基类中所有域和方法。例如:

//: reusing/Detergent.java
// Inheritance syntax & properties.
import static net.mindview.util.Print.*;

class Cleanser {
  private String s = "Cleanser";
  public void append(String a) { s += a; }
  public void dilute() { append(" dilute()"); }
  public void apply() { append(" apply()"); }
  public void scrub() { append(" scrub()"); }
  public String toString() { return s; }
  public static void main(String[] args) {
    Cleanser x = new Cleanser();
    x.dilute(); x.apply(); x.scrub();
    print(x);
  }
}	

public class Detergent extends Cleanser {
  // Change a method:
  public void scrub() {
    append(" Detergent.scrub()");
    super.scrub(); // Call base-class version
  }
  // Add methods to the interface:
  public void foam() { append(" foam()"); }
  // Test the new class:
  public static void main(String[] args) {
    Detergent x = new Detergent();
    x.dilute();
    x.apply();
    x.scrub();
    x.foam();
    print(x);
    print("Testing base class:");
    Cleanser.main(args);
  }	
} /* Output:
Cleanser dilute() apply() Detergent.scrub() scrub() foam()
Testing base class:
Cleanser dilute() apply() scrub()
*///:~

即使是一个程序中含有多个类,也只有命令行所调用的那个类的main()方法会被调用

作者:KeySv

出处:https://www.cnblogs.com/cc-coding/p/16527254.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   我只有一天的回忆  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu