《Java编程思想》笔记3:初始化,访问控制
本篇关注:初始化(第5章),访问控制(第6章)
初始化 Initialization
构造器 constructor
创建对象时被自动调用,名称与类名完全相同的特殊方法,用来执行初始化,没有返回值
Java中,创建和初始化是捆绑的。
new创造对象时,会为对象分配空间,并调用相应构造器。
默认构造器
又叫无参构造器。如果类中没有写,则编译器会自动创建。
class Rock {
Rock() { // 默认构造器
...
}
}
可以修改为有参数
重载 Oveloading
用多种方式创建一个对象。参数列表必须独一无二。以返回值区分重载方法是行不通的。
class Tree {
int height;
Tree() {
System.out.println("Planting a seed");
height = 0;
}
Tree(int initialHeight) {
height = initialHeight;
System.out.println("The new tree is " + height + " feet tall");
}
void info() {
System.out.println("Tree is " + height + " feet tall");
}
void info(String string) {
System.out.println(string + ": Tree is " + height + " feet tall");
}
}
- 如果传入的数据类型(实际参数类型)小于方法中声明的形式参数类型,实际数据类型就会被提升。char型不同,如果无法找到接受char参数的方法,就会把char提升至int。
- 如果传入的实际参数较大,就得通过类型转换来执行窄化转换。
this
this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用
很容易在一条语句里对同一个对象执行多次操作。或者将其自身传递给外部方法。
在构造器中调用构造器
this加上参数列表,可以调用符合的构造器。
public class Flower {
int petalCount = 0;
String s = "initial value";
Flower(int petals) {
petalCount = petals;
System.out.println("Constructor with int arg only");
}
Flower(String s, int petals) {
this(petals);//call constructor <Flower(int petals)>
//! this(s);//can't call two
this.s = s;
System.out.println("Constructor with int & String args");
}
Flower() {
this("hi", 47);
System.out.println("Default constructor with no arg");
}
}
这种调用必须处在构造器块中的起始处。
不可以同时调用两个构造器。
this.s 可以指代数据成员。
其他方法中不可以调用构造器。
成员初始化
基本数据类型都有默认的初值。对象引用的默认值是null。
声明变量的时候直接赋值,或者调用某方法为它赋值。
类的内部,变量定义的先后顺序决定了初始化的顺序。
变量定义于任意位置,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。
静态初始化只在必要时刻(静态对象被创建/静态数据被访问),在class对象首次加载的时候进行一次。
可以使用静态块组合多个静态初始化
static int a;
static int b;
static{
a = 3;
b = 4;
}
初始化顺序:先是静态对象,再是非静态对象。
构造器也属于静态方法。
对象创建过程:假设有个Dog类
- 首次创建Dog对象,或者首次访问Dog类的静态方法/数据时,定位Dog.class文件
- 载入Dog.class(创建Dog.class对象)。执行静态初始化,之后不会执行第二次。
- 用new Dog()创建对象时,在heap上分配内存空间
- 内存空间清零,Dog对象所有基本数据类型和引用变为默认值
- 执行程序中定义的初始化
- 执行构造器
可变参数列表
参数个数或类型不确定时
显式地创建以Object数组为参数的方法:
class A {}
public void printArray(Object[] args) {
for(Object obj : args)
System.out.print(obj + " ");
System.out.println();
}
printArray(new Object[]{new Integer(47), new Float(3.14), new Double(11.11)});
printArray(new Object[]{"one", "two", "three"});
printArray(new Object[]{new A(), new A(), new A()});
定义可变参数列表:
public void printArray(Object... args) {
for(Object obj : args)
System.out.print(obj + " ");
System.out.println();
}
类型确定:
public void printArray(int i, String... args) {}
适用于参数个数不确定的情况,java把可变参数当做数组处理。
- 只支持有一个可变参数,只能出现在参数列表的最后;
- ...前后有无空格都可以,args名字自定义。
- 方法中以数组的形式访问可变参数。
- 把0个参数传给可变参数列表也可以。
枚举类型 Enumerated types
public enum Spiciness {
NOT, MILD, MEDIUM, HOT, FLAMING
}
public class SimpleEnum {
public static void main(String[] args) {
Spiciness spiciness = Spiciness.MEDIUM;
System.out.println(spiciness);
for(Spiciness s : Spiciness.values())
System.out.println(s + ", ordinal " + s.ordinal());
}
}
可以把enum看成是一个类。一个数据类型。
通常枚举的元素命名全都大写,多个单词用_隔开。
但是enum不能使用extends继承其他类,因为enum已经继承了java.lang.Enum(Java是单一继承)
枚举类中每个值都被映射到protected Enum(String name, int ordinal)
ordinal():获取枚举常量的声明顺序
values():获取一个数组,按声明顺序
与switch组合绝佳,因为switch是在有限的可能值集合中选择
switch(degree) {
case NOT:
System.out.println("not spicy at all");
break;
case MILD:
case MEDIUM:
System.out.println("a little hot");
break;
case HOT:
case FLAMING:
default:
System.out.println("may be to hot");
break;
}
EnumSet
提供了Set接口的实现
EnumSet allSpiciness = EnumSet.allOf(Spiciness.class);
EnumSet partSpiciness = EnumSet.of(Spiciness.NOT, Spiciness.MEDIUM);
EnumMap
提供了Map接口的实现。键(key)是一个枚举类型
EnumMap<Spiciness, String> enumMap = new EnumMap<>(Spiciness.class);
enumMap.put(Spiciness.NOT, "not spicy at all");
enumMap.put(Spiciness.MILD, "a little hot");
enumMap.put(Spiciness.HOT, "maybe too hot");
for(Spiciness s : Spiciness.values())
System.out.println(s + " represents " + enumMap.get(s));
访问控制 Access control
.java文件被称为编译单元 compilation unit,或转译单元 translation unit
每个编译单元只能有一个public类
编译后产生同名的.class文件。Java的可运行程序就是一组.class文件(可打包为JAR)
package:声明一个包。位于除了注释外的第一行。
import:导入一个包。
包名:全部小写。通常是把自己的域名反过来。或者是机器上的class文件路径。
处于同路径且没有声明包名的class文件,会被看作隶属于该目录的默认包中。
如果import两个包,包含相同的类名。创建时就必须明确指明:
java.util.Vector v = new java.util.Vector()
类中成员(变量/方法)访问权限:
类内部 | 本包其他类 | 外部包子类 | 外部包其他类 | ||
---|---|---|---|---|---|
public | √ | √ | √ | √ | |
protected | √ | √ | √ | ||
default | √ | √ | |||
private | √ |
default:默认的包访问权限,不需要写出来。class Abc{ }
类的修饰:不可以是private和protected的。内部类可以。
不希望别人访问该类,可以把构造器设为private。但是public static成员可以被访问。
通常最好是把域保持为private,通过protected方法控制类的继承者的访问权限。
封装 encapsulation:把数据和方法包装进类中,隐藏具体实现。得到一个数据类型。