1.this关键字的用法
💗在成员方法内部,this表示对“调用方法的那个对象”的引用
💫方法形参或局部变量与成员变量同名时,成员变量被隐藏,通过“this.成员变量名”使用成员变量
💫this可作为返回值,返回对当前对象的引用
💗在构造方法内部,通过this调用当前类的另一个构造方法(见“构造方法重载”)
💗在成员方法内部
public class Leaf {
int i;
Leaf(int i){
this.i = i; //任何一个对象除了基本的成员变量以外,还有一个区域是this,this是个对象,指向自身
}
Leaf increment() {//成员方法,返回值为Leaf类型
i++;
return this;
}
void print() {
System.out.println("i = "+i);
}
public static void main(String[]args) {
Leaf leaf = new Leaf(100);
leaf.increment().increment().print();//102
//为了便于理解,上面这一句等效于下面三句
Leaf leaf2 = leaf.increment();
Leaf leaf3 = leaf2.increment();
leaf3.print();
}
}
进入leaf(int i){}
跳出leaf(int i){},结束Leaf leaf = new Leaf(100);局部变量被销毁。
leaf.increment().increment();
💗在构造方法内部
public class Leaf {
int i;
Leaf(int i){
this.i = i;
}
Leaf(){
this(1);//在构造方法内部,通过this调用当前类的另一个构造方法
//等价于在main函数中Leaf(1),但构造方法只能和new一起
}
public static void main(String[]args) {
Leaf leaf = new Leaf();
}
}
2.super关键字的用法
super和this类似,都是在堆中的对象里占用空间。
💗表示“当前对象的父类对象的引用”,用来引用父类中的成员变量或方法。
💗在子类构造方法中,通过“super([参数列表]);”调用父类的构造方法。该语句必须出现在子类构造方法的第一行。
💗对当前对象的父类对象的引用
public class TestInheritance{
public static void main(String[] args){
Rectangle rect=new Rectangle();
rect.newDraw();
}
}
class Shape{
public void draw(){ System.out.println("Draw shape"); }
}
class Rectangle extends Shape{
public void draw(){ System.out.println("Draw Rectangle"); }
public void newDraw(){
draw();
super.draw();
}
}
输出结果为:
Draw Rectangle
Draw shape
package java001;
class FatherClass{
public int value;
public void f() {
value=100;
System.out.println("FatherClass,value:"+value);
}
}
class ChildClass extends FatherClass{
public int value;
public void f() {
super.f();
value = 200;
System.out.println("ChildClass.value:"+value);
System.out.println(value);
System.out.println(super.value);
}
}
public class TestInherit {
public static void main(String[]args) {
ChildClass cc = new ChildClass();
cc.f();
}
}
💗调用父类的构造方法
package java001;
class SuperClass{
private int n;
SuperClass(){
System.out.println("SuperClass()");
}
SuperClass(int n){
System.out.println("SuperClass("+n+")");
this.n=n;
}
void getSuperN() {
System.out.println("Super N:"+n);
}
}
class SubClass extends SuperClass{
private int n;
SubClass(int n){
System.out.println("SubClass("+")");
this.n=n;
}
SubClass(){
//super();默认的
super(300);//调用父类的构造方法,一定要放在子类构造方法的第一行,如果不写这一行代码,电脑会自动默认调用父类的无参数的构造方法。
System.out.println("SubClass()");
}
void getSubN() {
System.out.println("Sub N:"+n);
}
}
public class TestSuperSub {
public static void main(String[]args) {
SubClass sc = new SubClass();
//可以这样理解:
//这句代码把SuperClass中的变量和方法空间创建出来,又创建了SubClass的变量和方法空间,调用SubClass()构造方法(进入SunClass()行数第一步就去调用SuperClass()构造方法)和SuperClass()构造方法。(如果没有super(300);会自动调用super();)
//父类优先
sc.getSuperN();
sc.getSubN();
}
}
💦子类对象的创建与初始化的一般步骤💦SubClass sc = new SubClass();
1.子类构造方法调用父类构造方法:
通过显式的super()方法调用或编译器隐含插入的super()方法调用(子类构造方法的第一句),这一过程递归地进行(多重继承关系时),直到根类Object的构造方法。在这一过程中,子类对象所需的所有内存空间被分配,所有成员变量均使用默认值初始化。
2.从根类Object的构造方法开始,自顶向下地对每个类依次执行如下两步:
显式初始化。
执行构造方法的主体(不包括使用super调用父类构造方法)。
3.static关键字(可修饰类成员变量和成员方法)
3.1静态变量
静态变量:在类的成员变量声明中带有static关键字的变量。
静态变量的创建:
- 静态变量的创建与实例对象无关。
- 只在系统加载其所在类时分配空间并初始化,且在创建该类的实例对象时不再分配空间。
代码建议仔细阅读,后面有注释,含有变量、构造方法调用的顺序。
package java004;
//在调用构造方法之前,先把对应类中的变量创建出来,若有static变量,按从上往下的顺序创建(调用构造方法),再创建普通变量
//若static变量之前创建过,再次遇到时跳过,而普通变量需要再次被创建、调用构造方法,如main函数中的两个new Cupboard();
class Bowl {
Bowl(int i) { System.out.println("Bowl(" + i + ")"); }
void f1(int i) { System.out.println("f1(" + i + ")"); }
}
class Table {
static Bowl bowl1 = new Bowl(1);
Table() {
System.out.println("Table()");
bowl2.f1(1);
}
void f2(int i) { System.out.println("f2(" + i + ")"); }
static Bowl bowl2 = new Bowl(2);
}
class Cupboard {
Bowl bowl3 = new Bowl(3);
static Bowl bowl4 = new Bowl(4);
Cupboard() {
System.out.println("Cupboard()");
bowl4.f1(2);
}
void f3(int i) { System.out.println("f3(" + i + ")"); }
static Bowl bowl5 = new Bowl(5);
}
public class StaticInitialization {//这个类包含Static变量,加载这个类的时候,首先要加载Static变量
public static void main(String[] args) {
System.out.println("new Cupboard() in main");
new Cupboard();//在调用构造方法之前,bowl3会再被创建一次,调用new Bowl(3),输出Bowl(3),而静态变量只创建一次,故不再调用
//调用构造方法,输出Cupboard()、f1(2)
System.out.println("new Cupboard() in main");
new Cupboard();//重复上面的new Cupboard();
table.f2(1);
cupboard.f3(1);
}
static Table table = new Table();//StaticInitialization类包含static变量,第一步,先执行这一句,在数据区创建table变量,接着在调用构造方法之前,先加载Table类的static变量,
//在数据区创建bowl1变量,Bowl类中无静态变量,调用构造方法,输出Bowl(1)。接着,在数据区创建bowl2变量,Bowl类中无静态变量,调用构造方法,输出Bowl(2)。
//静态变量加载完后,调用new Table()构造方法,输出Table()、f1(1);
static Cupboard cupboard = new Cupboard();//第二步,数据区创建cupboard变量,接着在调用构造方法之前,先加载Table类的static变量,
//在数据区创建bowl4变量,Bowl类中无静态变量,调用new Bowl(4)构造方法,输出Bowl(4)。接着,在数据区创建bowl5变量,Bowl类中无静态变量,调用构造方法,输出Bowl(5)。
//在栈中创建变量bowl3,Bowl类中无静态变量,调用new Bowl(3)构造方法,输出Bowl(3)。
//静态变量加载完后,调用new Cupboard()构造方法,输出Cupboard()、f1(2)
//进入main函数
}
非private的静态变量,可在类外用类名访问(一下两种均可)
class Employee{
private int id;
public static int serialNum = 1;
Employee(){ id=serialNum ++; }
}
class OtherClass{
public static void main(String[] args){
System.out.println(Employee.serialNum);
}
}
class Employee{
private int id;
public static int serialNum = 1;
Employee(){ id=serialNum ++; }
}
class OtherClass{
public static void main(String[] args){
Employee e=new Employee();
System.out.println(e.serialNum);
}
}
3.2静态方法
- 静态方法:在类的成员方法声明中带有static关键字的方法。
- main方法是静态方法、程序入口点,不创建实例对象就可以运行该方法。
- 静态方法一般通过类名访问,但特殊情况下也可通过实例对象访问。
class GeneralFunction {
public static int add(int x, int y) {
return x + y;
}
}
public class UseGeneral {
public static void main(String[] args) {
int c = GeneralFunction.add(9, 10);
System.out.println("9 + 10 = " + c);
}
}
静态方法中没有this引用,需要将实例传入参数才可访问。因此,静态方法不能直接调用实例方法,也不能直接访问所属类的实例成员变量,但可以访问静态变量和静态方法。
public class TestStaticMethod{
public static void main(String[] args){
StaticMethod obj=new StaticMethod();
StaticMethod.sPrintXAndY(obj); }
}
class StaticMethod{
int x=0; static int y=1;
public void iPrintAndIncreaseY(){
sPrintY();
y++;
}
public static void sPrintY(){
//System.out.println(this.x); //不能访问实例成员变量
//iPrintAndIncreaseY(); //不能访问实例方法
System.out.println(StaticMethod.y); //可以访问静态变量
}
public static void sPrintXAndY(StaticMethod o){
System.out.println(o.x); //可以通过o引用访问实例成员变量
o.iPrintAndIncreaseY(); //可以通过o引用调用实例方法
sPrintY(); //可以直接调用静态方法
}
静态方法的重写:
回顾方法重写的规则:
不改变方法的名称、参数列表和返回值,改变方法的内部实现
子类中重写方法的访问权限不能缩小
子类中重写方法不能抛出新的异常
父类中private的成员,在子类中不能被覆盖(重写)
本节加入以下重写规则:
子类不能把父类的静态方法重写为非静态
子类不能把父类的非静态方法重写为静态
子类可以声明与父类静态方法相同的方法
//静态方法的重写不会导致多态性
//构造方法可以看成是static方法,不具有多态性
4.final关键字
final的使用位置
1.在类声明中使用:表示类不能被继承。
2.在成员方法声明及方法参数中使用:成员方法不能被重写,参数变量值不能更改。
3.在成员变量和局部变量声明中使用:表示变量的值不能更改。
4.1在类声明中使用final关键字
被定义成final的类不能再派生子类
例: final class Employee {…}
class Manager extends Employee { …}
4.2在成员方法声明中使用final关键字
被定义成final的方法不能被重写。
//将方法定义为final可使运行时的效率优化
//对于final方法,编译器直接产生调用方法的代码,而阻止运行时刻对方法调用的动态绑定
private的方法都隐含指定为是final的,对子类不可见就无所谓被重写。
4.3在方法参数中使用final关键字
将方法参数指明为final,则无法在方法中更改参数的值
//Cat类含有name和eyeColor两个成员变量
public void m2(final int j) { // final变量不能改变值,final方法不能被重写
//j = 10; 报错
}
public void mCat(final Cat c) {
c.eyeColor = "black";//重要!!!!不报错,引用不能改变,但堆里的内容可以改变
//c = new Cat("name", "color"); 报错,指向新的引用
}
}
4.4在成员变量中使用final关键字
class T {
final int i = 8; //final的变量不能改变值, c++ -> const
public final void m() { //final方法不能被重写
//i = 8; 让i=8仍会报错,因为编译器会理解为你在试图改变i的值
}
}
空白final:若final成员变量声明时未赋初值,则在所属类的每个构造方法中都必须对该变量赋值。
class Poppet {
private int i;
Poppet(int ii) { i = ii; }
}
public class BlankFinal {
private final int i = 0; // 被初始化的final
private final int j; // 空白final
private final Poppet p; // 空白final引用
public BlankFinal() {
j = 1; // 初始化空白final
p = new Poppet(1); // 初始化空白final引用
}
public BlankFinal(int x) {
j = x; // 初始化空白final
p = new Poppet(x); // 初始化空白final引用
}
public static void main(String[] args) {
new BlankFinal();
new BlankFinal(47);
}
}
4.5在局部变量中使用final关键字
被定义成final的局部变量可以在所属方法的任何位置被赋值,但只能赋一次。