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访问权限修饰词#
public、protected和private这几个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 国际」许可协议进行许可。
本文来自博客园,作者:我只有一天的回忆,转载请注明原文链接:https://www.cnblogs.com/cc-coding/p/16527254.html
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术