0.
对象与类
构造函数 & 析构函数
对象构造
构造函数重载:
默认构造函数:
- 如果没有显式定义构造函数,则系统提供一个默认构造函数;若类中提供了任意的构造函数,则系统不再提供默认构造函数;
显式地域的初始化:
- 在类的定义中,将一个值赋给一个域:
class A{
public String name = "";
}
- 在执行构造函数之前,先执行上面的赋值;
- 当一个类的所有构造函数都希望将相同的值赋给同一个域时,使用上面的方法;
- 域的初始值不一定是常量,可以使用方法来初始化:
class A{
static String assignName(){
String name = "";
return name;
}
public String name = assignName();
}
- C++中使用初始化列表;
构造函数中调用其他的构造函数:
- 在构造函数的第一句使用语句:this(... ...):
class A{
A(String name){
... ...
}
A(String name1, String name2){
// call constructor A(String name)
this(name1);
... ...
}
}
- C++中不能在构造函数中调用其他的构造函数;
初始化块:
- 第三种初始化域的方法(其余1:在构造函数;2:在声明中显式地初始化域;);
- 在一个类的声明中,可以包含多个代码块,只要构造类的对象,这些代码块就会执行;首先运行初始化块,再运行构造函数:
class A{
A(String name){
... ...
}
int id;
// object initialization block
{
id = 1;
}
}
static域的初始化:
- 提供一个初始化值:
class A{
static int id = 1;
}
- 使用静态初始化块:
class A{
static int id;
static{
Random generator = new Random();
id = generator.nextInt(1000);
}
}
- 当类第一次加载时,将会进行静态域的初始化;
初始化顺序
静态变量、静态初始化块、变量、初始化块、构造函数的初始化顺序:
(静态变量、静态初始化块)> (变量、初始化块)> 构造函数
测试(继承关系的类的初始化顺序):
import java.util.*;
class Parent{
public String pField = "Parent - object initialization";
public static String psField = "Parent - static object initialization";
{
System.out.println(pField);
System.out.println("Parent - object initialization block");
}
static{
System.out.println(psField);
System.out.println("Parent - static object initialization block");
}
public Parent(){
System.out.println("Parent - default constructor");
}
public Parent(String str){
System.out.println("Parent - constructor");
}
}
class Child extends Parent{
public String cField = "Child - object initialization";
public static String csField = "Child - static object initialization";
{
System.out.println(cField);
System.out.println("Child - object initialization block");
}
static{
System.out.println(csField);
System.out.println("Child - static object initialization block");
}
public Child(String str){
//super(str); if add this sentence, will call the constructor Parent(String str), not call default constructor.
System.out.println("Child - constructor");
}
}
public class ObjectInitialzationTest{
public static void main(String[] args){
Parent p = new Child("Test");
}
}
输出:
静态变量与静态初始化块之间,变量与初始化块之间的初始化顺序是如何的呢?
测试:
import java.util.*;
class A{
public A(){
System.out.println("A - constructor");
}
}
class B {
public B(){
System.out.println("B - constructor");
}
}
public class ObjectInitialzationTest{
static A a = new A();
static{
System.out.println("static object initialization block");
}
static B b = new B();
public static void main(String[] args){
new ObjectInitialzationTest();
}
}
输出:
静态变量与静态初始化块之间(变量与初始化块之间)的初始化顺序取决于它们在类中的定义顺序。
静态块与静态成员的初始化工作与实例化过程无关。实例化之前需要初始化静态块和静态成员,但并不代表实例化一定会执行静态块和静态成员。有时候执行静态块或静态成员初始化不一定就是实例化对象时才进行的,例如调研该类的某静态成员或静态方法,又例如该类的子类被实例化或者调用了静态成员或静态方法等。
实例化的顺序是(不考虑静态成员/块):
- 进入当前类的构造函数;
- 进入父类构造函数递归直至java.lang.Object类的构造函数;
- 执行java.lang.Object类的构造函数:顺序依次为成员变量初始化和初始化块(视上下文顺序),对应的构造函数体;
- 重复上一条步骤直至当前类
对象析构
Java不支持析构函数。
(可以为类添加一个finalize方法,finalize方法将在垃圾回收器清除对象之前被调用。实际应用中,尽量不要使用。)
1.2 域&方法
1.2.1 静态域与方法
静态域:
- 静态域用的最多的是静态常量:
class A{
private static final double PI = 3.1415926;
}
静态方法(同C++):
- 静态方法没有隐式的this参数;
- 不能访问实例域,只能访问静态域;
1.2.2 final实例域
final实例域必须被初始化:
- 也就是说,必须确保在每一个构造函数执行之后,这个域的值被设置。
final修饰符大都用于基本类型或不可变的类:
- 如果类中的每个方法都不会改变这个对象,这种类就是不可变类,如String;
- 若应用于可变对象,如:final Date time; 仅仅意味着存储在time变量中的对象引用在初始化之后不可改变,但并不意味其是一个常量,可以通过setTime等方法修改time变量所引用的对象;
1.3 访问权限
继承
阻止继承:final类和final方法
- 不允许扩展的类称为final类(如String),使用修饰符final(final类中的方法自动为final):
final class Person{
... ...
}
例如:java.lang.String
public final class String
extends Object
implements Serializable, Comparable<String>, CharSequence
- 类中的方法也可以被声明为final,子类不能覆盖这个方法;
抽象类
- 包含一个或多个抽象方法的类本身必须被声明为抽象的:
abstract class Person{
... ...
public abstract String getDescription();
}
- 除了抽象方法之外,抽象类还可以包括具体方法和具体域;
- 即使不包含抽象方法也可以声明为抽象类;
- 在具体的实现子类中,扩展类有两种选择:(1)实现全部的抽象方法,这样子类就不是抽象的了;(2)实现部分或不实现抽线方法,这样必须将子类也标记为抽象类;
接口
接口不是类,是一组对类的需求描述;
- 接口中的方法自动属于public;(但在接口的实现类中,需要指定方法的访问属性)
- 接口不能定义实例域;
- 接口中的域自动为public static final;
- 不能在接口中实现方法;
- 类可以实现多个接口,之间用,分割;
- 接口可以继承其他的接口;
接口 V.S. 抽象类
- 接口类可以用来实现多重继承;
2. 内部类
内部类时定义在另一个类中的类。
(编译器会把内部类翻译成用$分隔外部类名与内部类名的常规类文件:TalkingClock$TimePrinter.class)
- 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据;
- 内部类可以对同一个包中的其他类隐藏起来;
- 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷;
2.1 内部类规则
- 内部类的对象有一个隐式引用,它引用了实例化该内部对象的外围类对象。static内部类没有这种附加指针。(但是这个引用在内部类的定义中是不可见的,该引用是在内部类构造时引入的)
- 只有内部类可以时私有类,而常规类只能具有包的可见性,或公有的可见性;
- 内部类既可以访问自身的数据域,也可以访问创建它的外围类对象的数据域;
class TalkingClock{
public TalkingClock(){
}
public void start(){
ActionListener listener = new TimePrinter();
......
}
private int interval;
<strong>private boolean beep;</strong>
private class TimePrinter implements ActionListener{
public void actionPerformed(ActionEvent event){
// data member in TalkingClock
<strong>if (beep)</strong>{
... ...
}
}
}
}
- 使用外围类引用的正规语法: OuterClass.this
public void actionPerformed(ActionEvent event){
// data member in TalkingClock
if (<strong>TalkingClock.this.beep</strong>){
... ...
}
}
- 构造内部类的正规语法: OuterObject.new InnerClass(construction parameters)
public void start(){
<strong>ActionListener listener = this.new TimePrinter();</strong>
......
}
- 在外围类的作用域之外,引用内部类:OuterClass.InnerClass
TalkingClock jabberer = new TalkingClock();
<strong>TalkingClock.TimePrinter listener = jabberer.new TimePrinter();</strong>
2.2 局部内部类
定义一个块中类;
- 局部类不能用public或private访问说明符进行声明,它的作用域被限定在声明该局部类的块中。
- 局部类对外部完全隐藏,即使TalkingClock(外部类)中的其他代码也不能访问它。
class TalkingClock{
public TalkingClock(){
}
public void start(){
<strong>class TimePrinter implements ActionListener{
public void actionPerformed(ActionEvent event){
.....
if (beep){
......
}
}
}</strong>
ActionListener listener = new TimePrinter();
......
}
private int interval;
private boolean beep;
}
- 局部类不仅能访问包含它的外围类的域,还能访问局部变量,不过,这些局部变量必须被声明为final
class TalkingClock{
public TalkingClock(){
}
<strong>public void start(final boolean beep){
class TimePrinter implements ActionListener{
public void actionPerformed(ActionEvent event){
.....
if (beep){
......
}
}
}</strong>
ActionListener listener = new TimePrinter();
......
}
}
2.3 匿名内部类
假如只创建这个类的一个对象,就不必为它命名了,这种类成为匿名内部类;
- 因为构造函数的名字必须与类名相同,而内部类没有类名,所以,匿名类不能有构造函数;
class TalkingClock{
public TalkingClock(){
}
public void start(final boolean beep){
<strong>ActionListener listener = new ActionListener(){
public void actionPerformed(ActionEvent event){
.....
if (beep){
......
}
}
};</strong>
......
}
}
2.4 静态内部类
有时候,使用内部类仅仅是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外部类对象,为此,可以将内部类声明为static,以便取消产生的引用;
(声明在接口中内部类自动成为static和public)
class ArrayAlg{
public static class Pari{
... ...
}
}