Java学习笔记 面向对象(下)
1.this与super
-
this
-
this.域变量和this.成员方法
明确表示用的是类的域变量public class Test{ int i; public otherMethod(){ int i = 3; this.i = i+2; } } -
this(参数)
引用重载的构造方法 -
this指代当前对象
/*已设定class FighterPlane,其中有成员方法public void setA(A _a) 和域变量private A a*/ public void setA(A _a){ if(_a!=null) {return a;} else return null; } class A{ FighterPlane fp; public A(FighterPlane fpp){ this.fp = fpp;//this获得了FighterPlane对象的引用 fpp.setA(this);//this指代当前的A对象 } } //主程序main中有以下语句 public static void main(String[] args){ FighterPlane ftp = new FighterPlane();//构造方法初始化 A a = new A(ftp); /*在class A的构造方法中,this指代a, 并利用fpp.setA(this)将A对象传入fpp对象中, 实现两个对象在其内部属性中都有对方的引用(两个类互相关联)*/ }
-
-
super
super 指代父类中的域变量或方法;当子类的域变量名和方法名与父类的相同时,调用父类的同名域变量或方法要用super- super.域变量和super.成员方法
- super(参数)
ATTENTION: this可以指代当前对象,super不可以指代父类对象
class A{ int x = 4;int y = 1; public void Printme(){ System.out.println("x="+x+"y="+y); System.out.println("class name: "+getClass().getName()); } } public class AA extends A{ int x; /* 子类和父类定义了同名域变量,子类继承了父类的x,又定义了x, 在生成子类对象时,子类对象将父类定义的同名域变量隐藏, 在子类所用的x均为子类自己定义的, 若使用父类定义的x,需采用super.x的形式 */ public void Printme(){ int z = super.x + 6; super.x = 5; super.Printme();//将父类中被覆盖的方法保留 System.out.println("I am an "+getClass().getName()); x = 6; System.out.println("z="+z+"x="+x+"super.x="+super.x="+super.x+"y="+y+"super.y="+y); } public static void main(String arg[]) { int k; A p1 = new A(); AA p2 = new AA(); p1.Printme(); p2.Printme(); } } /*运行结果如下 x=4 y=1 class name:A x=5 y=1 class name:AA I am an AA z=10 x=6 super.x=5 y=1 super.y=1 */
2.构造方法的多态
-
构造方法的重载
主要指构造方法可以被重载,一个类的若干重载的构造方法之间可以相互调用,需要用this关键字,同时这个调用语句应为整个构造方法的第一个可执行语句
class AddClass{ public int x = 0,y = 0,z = 0; AddClass(int x){ this.x = x; } AddClass(int x,int y){ this(x); this.y = y; } AddClass(int x,int y,int z){ this(x,y);this.z = z; } public int add(){ return x + y + z; } } public class RunAddClass{ public static void main(String args[]){ AddClass p1 = new AddClass(2,3,5); AddClass p2 = new AddClass(10,20); AddClass p3 = new AddClass(1); System.out.println("x+y+z="+p1.add()); System.out.println("x+y="+p2.add()); System.out.println("x="+p3.add()); } } /*运行结果如下 x+y+z=10 x+y=30 x=1 */ -
构造方法的继承调用
子类可以调用父类的构造方法,调用时使用关键字super
用法如下:- 调用父类构造方法不可以直接使用其名
- 若父类构造方法含参数,子类在自己构造方法中调用时,需使用super并将调用语句作为子类构造方法的第一个可执行语句
- 子类中没有显示地用super调用父类的构造方法,也没有用this重载构造方法时,默认调用父类无参数构造方法
public class SonAddClass extends AddClass{ int a = 0,b = 0,c = 0; SonAddClass(int x){ super(x);//调用父类构造方法不可以直接使用其名 a = x+7; } SonAddClass(int x,int y){ super(x,y);//构造方法含参数,super放在构造方法的第一条代码 a = x+5;b = y+5; } SonAddClass(int x,int y,int z){ super(x,y,z); a = x+4;b = y+4;c = z+4; } public int add(){ System.out.println("super:x+y+z="+super.add()); return a+b+c; } } public class RunAddClass{ public static void main(String args[]){ AddClass p1 = new SonAddClass(2,3,5); AddClass p2 = new SonAddClass(10,20); AddClass p3 = new SonAddClass(1); System.out.println("a+b+c="+p1.add()); System.out.println("a+b="+p2.add()); System.out.println("a="+p3.add()); } } /*运行结果如下: super:x+y+z=10 a+b+c=22 super:x+y+z=20 a+b=40 super:x+y+z==1 a = 8 */
Tips: 作为对象属性时,各种数据的初值(类的静态属性也依据下标赋初值)
| 成员变量 | 初值 |
|---|---|
| byte,shprt,int | 0 |
| long | 0L |
| float | 0.0F |
| double | 0.0D |
| char | ‘\u0000’ |
| boolean | false |
| String等引用声明 | null |
ATTENTION:
- 类域变量不赋初值在编译时不提示,成员方法中的变量不赋初值会提示
- 子类中调用this或super语句,不同时存在,位于第一句
3.抽象类
1) 抽象类中可以有0个或多个抽象方法,也可以有非抽象方法
2)有抽象方法必须用abstract修饰,不出现的时候可用可不用
3)抽象类不可以创建对象,创建对象由派生类的具体子类(继承)实现
4)抽象类可以有自己的声明,声明可以引用所有具体子类的对象,但只能使用子类从抽象类中继承过来的方法
5)抽象方法在抽象类中只指定其方法名,不写实现代码
6)抽象类中,非抽象方法(已实现的方法)可以调用抽象方法
7)抽象类必派生子类,若子类为具体子类则必须实现抽象类中的定义的所有抽象方法,若子类仍未抽象类,则子类抽象方法不可与父类抽象方法同名
8)abstract不可与final并列修饰同一个类(final最终类不可被继承)
4.接口(interface)
#接口可以指能被引用调用的方法,也可以指interface这个专有概念
-
接口的格式:
[public] interface 接口名[extend父接口名列表] { //静态常量数据成员声明 [public][static][final]域类型 域名 = 常量值 //抽象方法声明 [public][abstract]返回值 方法名(参数列表)[throw 异常列表] }1)interface前修饰符只能为public或默认
2)接口中定义的数据成员全是静态常量,不写修饰符也默认为final static(表明没有对象),没有域变量
3)接口无构造方法,所有成员方法均为抽象方法,不写修饰符也默认为abstract,因此方法前不能用final修饰
4)接口可以通过extends关键字继承父接口
5)接口和其子类不可以有自己的实力对象,可以有自己的声明,可以引用实现接口或子接口的类对象 -
接口的实现
1)用implements实现,实现接口的类前修饰符必须使用public
2)一个类实现多个接口时,implements后用逗号隔开多个接口的名字,一个接口也可以由多个类实现
3)实现接口的类继承接口中定义的常量
使用常量:“类名.常量” 、“引用名.常量”、“接口名.常量”
4)实现接口的不是抽象类时,则必须实现接口中所有的抽象方法,抽象类可以不全部实现,但其具体子类必须将接口中剩余抽象方法实现完
例一:用接口和继承实现计算正方形对角线
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.Math;
//定义接口
interface function{
public double calculate();//默认为抽象方法
}
//接口实现
class rectangle implements function{
public int a;public int b;
public rectangle(int A,int B){
a = A;
b = B;
}
public double calculate(){ //实现接口中的抽象方法
return (Math.pow(Math.pow(a,2)+Math.pow(b,2),0.5));
}
}
class square extends rectangle{
int a;
public square(int n) {
super(n,n); //调用父类构造方法
a = n;
}
public double calculate(){
return (Math.pow(Math.pow(a,2)+Math.pow(a,2),0.5));
}
double area()
{ return Math.pow(a,2); }
}
public class lenth {
public static void main(String args[])throws IOException {
BufferedReader din = new BufferedReader(new InputStreamReader(System.in));
int n;double l;
n = Integer.parseInt(din.readLine());//前三行为了实现输入数组
//使用接口
function f = new square(n);
l = f.calculate();
System.out.print(l);
}
}
ATTENTION: 不可以通过接口调用实现接口的类独有的方法,如f.area(),会报错
5.引用
-
声明可以引用所有具体子类的对象,但只能使用子类从抽象类中继承过来的方法
-
引用替换规则:父类声明可以引用所有具体子类对象,并可以被替换,子类声明不可以引用平行级别的其他类对象,也不可引用父类对象
-
父类声明和子类声明引用同一个子类对象的区别:父类可以通过显示的转化赋值给子类声明,子类声明引用的子类对象赋值给父类不需要显示的转化
//A是父类,A1,A2是子类 A a = new A(); //a是A的引用对象 a = new A1(); //a又引用A1对象,引用被替换 A1 a1 = (A1)a; //显示转化,a1通过a也引用了A1对象 a = new A2(); //a放弃引用A1,引用A2对象 A aa = a1; /*子类A1对象通过a1赋值给父类声明aa,aa也引用了A1对象 但只可以引用A1中从A继承来的属性和方法 */WRONG:
A1 a1 = new A2(); //错误,平行级间不可以相互引用 A1 a1 = new A(); //错误,子类不可引用父类 -
引用比较
-
equals 方法(是object类的方法),比较两个引用是否相同
if(p1.equals(pp)) //参数为对象 {System.out.println("p1和pp引用相同")}但String类,包装类、URL类会将其覆盖,虽然参数传入对象引用,但比较他们的值
-
使用 “= =” 比较
1)若两边为对象引用,则比较引用是否相同
2)若两边是数值,则比较数值是否相同
3)若两边是数值且类型不同,可能发生类型转换(10==10.0返回true)
4)若一边是引用一边是值,编译错误 -
instanceof比较引用类型
a(对象引用) instanceof A(类);
1)若a是A或A的子类的实例,返回true
2)若a是A父类的实例,返回false
3)若a和A无关系,编译错误
-
6.类的其他相关内容
-
内部类
即在某个类内部又定义了一个类,被内部类嵌入的叫外部类1)内部类定义在外部类域的位置,可以被private、protect等修饰符修饰,若定义在方法内则不可被任何修饰符修饰,并只能访问方法体内的常量,不可访问方法体内的局部变量
2)内部类可以访问外部类所有属性,包括private修饰的属性和方法;外部类不可以直接访问内部类中的成员
3)内部类前修饰符不为public时,需从外部类的特定接口方法来得到内部类,如上述代码中的 getInner();
4)static修饰内部类时,内部类相当于外部类的一个静态属性,内部类中可以有static属性或方法,static内部类不可以使用外部类中非静态的属性或方法;产生对象的方式如下Outer.Inner inner = new Outer.Inner();5)非静态的内部类如果不是定义在静态方法中,就不能在类的静态方法中调用
6)内部类也可以是抽象类//public class Inner 嵌在class Outer中 //Outer 中有一个方法是Inner getInner(){return new Inner();} public static void main(String[] args){ Outer outer = new Outer(); //先产生外部类对象 //Outer.Inner inner = outer.getInner(); //若内部类前没有public,用这种方式访问 Outer.Inner inner = outer.new Inner(); //利用外部类对象引用产生内部类实例 } -
匿名
-
匿名对象
在对象创建时没有显示地为其指定引用的对象
匿名对象方法调用方式为直接调用而不是通过引用//原始 Person p1 = new Person(); p1.shout(); //匿名对象 new Person.shout();使用条件:
1)对一个对象只需要进行一次方法调用
2)将匿名对象作为参数传递给一个方法
(如下面第三组代码中 new Outer().callInner(new inner()); ) -
匿名内部类
-
用法
1)匿名内部类不能有构造方法
2)若匿名内部类继承了一个只含有参数的构造方法的父类,则创建它的对象时,在括号中必须带上这些参数
3)匿名内部类不能定义静态成员和方法
4)匿名内部类不能被public、protect、private、static修饰
5)只能创建匿名内部类的一个实例 -
格式:
new 类 A (){ 方法体 }//此时产生的是类A的子类对象 或 new 接口 A(){ 方法类 }//此时产生的是接口A的实现类对象- 使用条件
1)只用到类的一个实例
2)类在定义以后马上用到
3)类很小(4行代码以下)
public class Anonymity{ public static void main(String[] args){ Object obj = new Object(){ //定义了一个匿名类,匿名类覆盖了object类的hashcode方法 public int hashcode(){ return 42; } } System.out.println(obj.hashcode()); } }abstract class Anonymity{ abstract public void fun1(); }; public class Outer{ public static void main(String[] args){ //产生抽象类的匿名具体子类对象,并非“抽象类可以实例化” new Outer().callInner(new Anonymity(){ public void fun1(Anonymity(){ System.out.println("匿名类测试") } }); } public void callInner(Anonymity a){ a.fun1(); } } //若改为有名内部类: abstract class Anonymity{ abstract public void fun1(); }; public class Outer{ public void callInner(Anonymity a){ a.fun1(); } public static void main(String[] args){ class inner extends Anonymity{ public void fun1(Anonymity(){ System.out.println("匿名类测试") } }; new Outer().callInner(new inner()); } } -
-