基础语法

利用extends来进行继承,例如:

public class Sub extends Base
{
...
}

以上表明Sub类继承Base类,具体继承还要分两种情况。

  • 如果在同一个包中,那么Sub继承了Base中的public,protected和默认访问级别的成员变量和方法
  • 如果在不同的包中,不继承默认访问级别的成员变量和成员方法,其他和上一个相同。

默认访问级别就是前面没有加任何关键字。

java不支持多继承,一个类只能继承于一个类,但是父类可以有多个子类,就像一棵树。下级继承会继承同一分支上所有的成员和方法。

java中有一个所有类的祖先叫object类,如果没有使用extends关键字那么会自动继承这个类。

方法覆盖

覆盖指的是父类中的函数在子类中重新写。要满足下列条件:

  • 子类方法名称,函数签名(包括参数位置)和返回类型都要和父类方法一致
  • 在子类中,必须要先覆盖函数,才可以重写函数
Base类中:
public void method()
{
...
}

Sub类中:
public int method()
{
...
}

这样会报错

如果Sub类这样
public void method()
{
...
}
public int method()
{
...
}
这样可以编译通过
  • 子类方法不可以缩小父类的访问权限,如果父类的方法时private那么子类就不能写public
  • 子类不能抛出比父类更多的异常
  • 子类无法覆盖父类的静态方法,只能够隐藏。这两者的区别是覆盖的话父类可以使用子类的,子类也可以使用父类的。隐藏的话父类只能使用父类的,子类只能使用子类的。这是由于static定义的区域所决定的。
    例:
package newpackage;

public class Base
{
static void method()
{
System.out.println("Base");
}
void method2()
{
System.out.println("Base");
}
}
--------------------------------
package newpackage;
public class newsec extends Base
{
static void method()
{
System.out.println("Sub");
}
void method2()
{
System.out.println("Sub");
}
public static void main(String[] args)
{
Base a = new newsec();
a.method();
a.method2();
}
}

输出时Base和Sub
  • 可以扩大访问权限。并且如果是抽象类,子类可以只扩大访问范围而不做任何事.但是必须要实现它,不然只能让子类也定义成抽象类。

例如:

父类定义类A()的抽象方法,子类可以这样

public void A()
{

}
这也算实现

super

super和this一样都是指向一个对象,不同的是super指向父类,this指向自己。

使用场合:

  • 局部变量和类变量重名
  • 子类覆盖了父类的方法或成员变量时,可以用这种方法使用父类的方法

需要注意,如果父类成员变量和方法被定义成private,那么子类无论如何都无法访问他们。不能再静态方法区使用super关键字。

此外如果在父类第一行使用super()是调用父类构造函数

多态

多态就是父类可以使用子类,子类也可以使用父类(自我理解)。下面讲具体使用规范。
儿子可以用爸爸一切公有的,而爸爸如果引用了自己的实例就不能用儿子的,只有引用了儿子的实例才可以使用儿子继承于爸爸的。

  • 对于一个引用类型变量,编译器按声明变量进行处理。例如
Base who = new Sub();
who.subVar = "123";
这里编译会出错,无论后面引用了谁,who都是Base类,而父类不能直接使用子类的变量
  • 编译器允许继承分支关系的类进行类型转换。对于向上转型(子类使用父类的),编译器会自动类型转换。对于向下转型则需要强制类型转换。在运行时,子类可以转换成父类,但是父类实际上无法转换成子类。因为父类有的子类一定有,子类有的父类却不一定有。
  • 对于引用对象绑定,遵循如下规则

    1. 对于实例方法和引用变量是由实际使用的对象进行绑定。这个是动态的,也就是说方法的实际输出是由实际类型所决定。例如 Base a = new Sub();输出时Sub的。如果这个时候Sub的子类b = a;那么输出还是Sub的。但是如果使用了子类中独有的方法会报错
    2. 静态方法和成员变量由前面定义的类型所决定,这属于静态绑定,在编译时期已经决定。无论引用了谁都不会改变。

继承的使用

继承树的最上层一般是抽象层。并且继承树上有接口类型,那么尽可能的把应用变量声明成继承树上层的接口类型。因为实例可以是子类,向上转型不会出现问题,这样便于使用多个子类,如果想用子类独有的东西就把引用变量定义成子类。

继承关系会打破封装,因为上层类改变会使下层类也跟着改变。所以对上层类定义要谨慎。

修饰符

修饰符 成员方法 构造方法 成员变量 局部变量
abstract(抽象的)
static(静态的)
public
protected
private
synchronized(同步的)
native(本地的)
transient(暂时的)
volatile(易失的)
final(不可改变的)

表中的类指的是顶层类,与内部类相对应(内部类是定义在类或方法中的类)。

访问控制修饰符

访问控制指的是其他类或对象可以查看的东西。一共有四种访问级别

  • public 所有都是可以给外部看的
  • protected 只对子类和一个包中的类公开
  • 默认级别 没有修饰符 只对同一个包中的类公开,不对子类公开
  • private 不对外公开

public>protected>默认>private。public与其他不同是其他包也可以查看

顶层类只能是public或protected,不然会出现编译错误。

访问级别是对成员变量和类来说的,对局部变量没有意义,局部变量的访问范围就是方法内部。

abstract

用abstract修饰的类表示抽象类,抽象类不允许实例化,也就是抽象类不允许创建实例

用abstract修饰的方法表示抽象方法,抽象方法内部没有内容,只有一个函数头。

注意,如果有抽象方法,这个类必须定义成抽象类。此外,没有抽象静态方法,abstract和static是互相矛盾的。

抽象类本身不可以实例化,但是可以创建一个抽象类的引用变量然后引用具体类。

Base a = new Base();//不可以,因为Base是抽象类
Base b = new Sub(); //Sub是具体类

抽象方法不可以用private修饰,因为抽象出来本来就是要让子类去实现的,private一下子类就无法使用了。

子类必须实现抽象类的所有抽象方法。

final

final有不可改变的含义。

用final修饰的类不能被继承,没有子类

用final修饰的方法不能被覆盖

final修饰的变量表示常量,只能赋一次值

final不能用来修饰构造方法。

final修饰引用变量,这个变量不能只能引用一个实例,但是可以对这个实例进行修改。

不允许覆盖就是不能用相同的函数签名去重写这个函数

static

  • static修饰的成员变量时静态变量,可以直接通过类名进行访问,并且所有实例共用一个静态变量
  • 修饰成员方法表示静态方法,可以直接通过类名访问
  • 修饰的程序代码块叫做静态代码块,加载类时会执行这些代码块

静态变量只有一份备份,处于方法区,在加载类的时候随之加载。

注意,在静态方法中没办法用this,因为静态方法时所有类共有的,this表示的是某个特定的类。所以在main方法中不能用this和super,因为main是static的,如果想访问这个类的内容,可以先定义一个该类的引用变量。

如果直接访问了,编译器会报在静态方法内不允许访问非静态变量

main函数必须是static的原因是只要加载了main所在的类main就被加载了,可以直接去执行,不然还要先创建一个类然后使用main方法。

类中可以包含一些静态代码块,这些代码块不存在与任何方法中,加载类时一次执行这些代码块。例如:

public class sample
{
static int i = 5;
static
{
System.out.println("First Static code i=" + i++);
}
}

static代码块和静态方法一样,不可以直接调用实例变量和实例方法,只用通过先引用之后才可以进行调用。

static进行静态导入

静态导入类似于c++中的using namespace std;可以简写代码。例如

import static java.lang.Integer.MIN_VALUE;
import.static java.lang.System.out;

public class TestStatic
{
public static void main(String[] args)
{
out.println(MIN_VALUE);//原来是Integer.MIN_VALUE
}
}

但是如果静态导入过多,可能会导致冲突,因此尽量导入的时候具体一点。