一、this关键字
当我们在调用某一方法时,常常采用 对象名.方法名(要传递的消息) 的方式调用。那如果我们用同一类型的两个对象分别调用同一个方法时,编译器是如何区分到底是哪一个对象调用的呢?
如:
public class Test{
public static void main(String[] args) {
Person p1 = new Person(1,20);
Person p2 = new Person(2,18);
p1.gender(1);
p2.gender(0);
}
}
class Person{
private int id;
private int age;
Person (int n,int i){
id = n;
age = i;
}
void gender(int x) {
.....;
}
}
实际上,编译器默默的将 “所操作对象的引用” 当做第一个参数传递给了要调用的那个方法。
所以上述例子 内部的表示形式实际为:
Person.gender(p1,1);
Person.gender(p2,0);
but我们不能这样编写代码,这是编译器内部“偷偷”传入的。
不过java为我们提供了一个关键字 就是—— this
**出现在类的方法定义中,只能在方法内部使用,表示对“调用方法的那个对象”**的引用。
需要注意:
1、当在方法内部调用同一个类另一个方法时,不需要使用this,直接调用即可,因为编译器会自己替你添加。
2、只有需要明确指出当前对象的引用时才使用this关键字。比如:需要返回对当前对象的引用时
3、this关键字也可以将当前对象传递给其他方法
4、处理成员变量和参数重名的情况,当成员变量和参数重名时,可以使用this关键字来避免歧义的发生。
public class Test{
public static void main(String[] args) {
Person p1 = new Person(1,20);
p1.eat(new Apple());
}
}
class Person{
private int id;
private int age;
Person (int id,int age){
this.id = id;//4、成员变量与参数重名的情况,this.id this.age 表示成员变量
this.age = age;
}
void eat(Apple apple) {
Apple peeled = apple.getPeel();
wow();//1,当在方法内部调用同一个类另一个方法时,不需要使用this,直接调用即可
}
void wow() {
System.out.print("yes!");
}
}
class Apple {
Apple getPeel() {
return Peeler.peel(this);//3、将当前对象(apple)传递给其他方法(方法peel)
}
}
class Peeler {
static Apple peel(Apple apple) {
return apple;
}
}
5、 this还有一种用法,在构造方法中调用构造方法。有时候一个类里面有多个构造方法(方法重载),为了避免重复代码,想在一个构造方法中调用另一个构造方法时可以用this。但要注意的是,不能在一个构造方法内部同时调用两个构造方法,且要将调用的构造方法放在最开始,否则编译会报错。
例如:
public class TestThis {
String s ;
int id ;
public static void main(String [] args) {
TestThis test1 = new TestThis();
System.out.println("String s is "+test1.s+",int id is "+test1.id);
}
TestThis (){
this("zhangsan",199);
System.out.println(3);
}
TestThis(String s,int n){
this(n);
this.s = s;
System.out.println(2);
}
TestThis(int id){
this.id = id;
System.out.println(1);
}
}
运行结果:
二、static关键字
在类中,被static声明的成员变量是静态变量,为该类的公用变量,在第一次使用时被初始化,对于该类的所有变量来说,static成员变量只有一份。
我们知道 类中的成员变量每new一个对象时就会产生一份成员变量,而被static修饰的成员变量始终只有一份,被该类的所有对象共享,它属于整个类,不属于专门的某一个对象。所以任何对象可以访问其类中的static值,类名+. 也可以访问static值。且每次访问的都是同一块内存
栗子:
public class CatStatic {
static int sid = 0;
String name;
int id;
public static void main(String [] args) {
CatStatic.sid = 99;//静态变量可以直接用类名访问
CatStatic cat1 = new CatStatic ("AAA");
cat1.info();
CatStatic cat2 = new CatStatic ("BBB");
cat2.info();
}
CatStatic(String name) {
sid++;
id = sid;
this.name = name;
}
public void info() {
System.out.println(name + " is "+id);
System.out.println("sid is "+ sid);
}
}
运行结果:
其中 static sid 在该程序有计数的功能,因为所有对象都可以访问sid,sid持续++。若sid 为非静态,要用对象名访问,且所有对象的sid都为1,就不具备计数功能了
用static声明的方法为静态方法,在调用该静态方法时,不会将对象的引用传递给它,**所以在静态方法中无法访问非静态成员。**因为非静态成员一定是属于某一个专门的对象的。
若将上面的代码改为
public class CatStatic {
int sid = 0;
…
}
则编译会报错
三、super关键字
super关键字
1、调用父类的构造方法
- 使用super();时,调用父类的无参构造方法
- 使用super(参数列表);调用父类的有参构造方法
注意: 调用父类构造方法是子类构造方法中需要做的第一步。
看图内容 重要!
2、调用父类中被重写的方法、变量
用一个例子来说明
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();//调用父类的法f()方法
value = 200;//修改子类对象的value值
System.out.println
("ChildClass.value="+value);
System.out.println(value);
System.out.println(super.value);//父类中的成员变量值已经从0变为100
}
}
public class TestInherit {
public static void main(String[] args) {
ChildClass cc = new ChildClass();
cc.f();
}
}
运行结果:
四、abstract关键字
被abstract关键字修饰的类叫做抽象类。
被abstract关键字修饰的方法叫做抽象方法。
abstract 使用规则:
- 抽象方法必须位于抽象类中(含有抽象方法的类必须被声明为抽象类或理解为含有抽象方法的类肯定是抽象类)
- 抽象类必须被继承
- 抽象方法必须被重写
- 抽象类不能被实例化
- 抽象方法只需要声明,不需要被实现。
规则好多绕了绕去,我们用一个例子来解释一下。(感谢“多态”贡献的例子✿✿ヽ(°▽°)ノ✿)
public class Test {
public static void main (String [] args) {
Animal a = new Animal("name");//4、该行会报错,因为Animal为抽象类,不能被实例化。
Cat c = new Cat("catname","blue");
Dog d = new Dog("dogname","black");
Kid k1 = new Kid ("xiaohua",c);
Kid k2 = new Kid ("xiaohong",d);
k1.myPetEnjoy();
k2.myPetEnjoy();
}
}
class Kid {
private String name;
private Animal pet;
Kid (String name, Animal pet) {
this.name = name;
this.pet = pet;
}
public void myPetEnjoy () {
pet.enjoy();
}
}
abstract class Animal {//2、类中含有抽象方法则该类必须为抽象类,否则编译会报错
//若无 abstract 修饰该类 则:错误: Animal不是抽象的, 并且未覆盖Animal中的抽象方法enjoy()
public String name;
Animal (String name){
this.name = name;
}
/*public void enjoy () {
System.out.println("Animal ......");
}*/ /* 1、该方法没有实现的必要,因为最终访问的是子类里的enjoy,
但有定义的必要,父类没有定义则不能重写,不能重写,无法实现多态。*/
public abstract void enjoy () ; //所以这里使用abstract 修饰该方法
}
class Cat extends Animal {
public String eyeColor;
Cat (String name,String eyeColor) {
super(name);
this.eyeColor = eyeColor;
}
public void enjoy () {//3、父类中含有抽象方法enjoy,则子类里必须重写该方法。否则编译报错,
System.out.println("Cat .....");
}
}
class Dog extends Animal {
public String furColor;
Dog (String name,String furColor) {
super(name);
this.furColor = furColor;
}
public void enjoy () {
System.out.println("Dog......");
}
}
五、interface关键字
interface(接口)使抽象的概念更向前迈进了一步。
前面所说的abstract关键字允许人们在类中创建一个或多个没有任何定义的方法(相当于提供了接口部分),但是没有提供任何相应的具体实现,这些实现是有该类的子类创建的。
interface关键字产生了一个完全抽象的类,它也不提供任何实现,允许创建者确定方法名、参数列表、返回类型,但是没有任何方法体。(相当于一种特殊的方法体,类中只有常量和方法的定义,而没有变量和方法的实现)接口只提供了形式,而不提供任何具体实现。
使用interface创建接口
public interface Runner {
public static final int id = 1;
public void start();
public void run();
public void stop();
}
让一个类遵循某一个特定的接口 需要用到implements关键字
class Test implements Runner{
.........
}
接口特性:
- 接口可以多重实现,即一个类可以实现多个接口,(一个类可以实现多继承了)
- 接口中声明的属性 默认为public static final 的,也只能是public static final 的。是为了修正C++多继承中会出现的问题,避免命名重复的冲突。
- 接口中的所有方法均为默认抽象方法。
- 接口中的方法默认为public的,也只能是public的。
- 接口可以继承其他的接口,并添加新的属性和抽象方法。
一个类可以实现多个接口、接口与实现类之间也存在多态性。
六、final关键字
~(ˇˍˇ) ~ 可能使用到final的三种情况:数据、方法、类。
1、 final数据
当需要一个永不被改变的编译时常量 或者 一个不希望被改变且在运行时被初始化的值 时
可以用final来修饰。
- final修饰的基本数据类型不可以改变,且对其定义时必须赋值。即成为了常量。
- final修饰的对引用不可以变。即一旦引用初始化指向一个对象就无法再把它改为指向另一个对象。但是对象本身是可以被修改的。
- final修饰的参数不可以改变。即你不能在方法中修改参数引用所指向的对象,但你可以读该参数(该特性主要用来向匿名内部类传递数据)。
2、final方法
当想把方法锁定,确保在所有子类无法修改方法的含义,使方法行为保持不变,并且不被覆盖时可以用final修饰。
注意:1、类中所有的private方法都隐式的指定为final的。
3、final类
当你不需要某一个类做任何改变,或者你不希望它拥有子类时,可以用final修饰。