本篇讲述面向对象其中两个重要思想:封装与继承,方法重写以及Object类的两个常用方法.
封装
是将类的某些信息隐藏在类的内部,不允许其他类及程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问,一般是public 修饰的get/set方法.
封装可以有效地提高代码的安全性,可维护性提高.
规则:将需要隐藏的元素用private修饰,本类中提供public修饰的get/set方法提供访问接口.
代码实例:
public class PrivateTest {
/*其他类无法访问到private修饰的变量.*/
private int num1=15;
private int num2=35;
/*提供get/set方法给其他程序访问接口
* 使用IDE可以自动生成,
* Eclipse是Alt+Shift+s→Getter/Setter
* IDEA是Alt+Insert→Getter/Setter.
* */
public int getNum1() {
return num1;
}
public void setNum1(int num1) {
this.num1 = num1;
}
public int getNum2() {
return num2;
}
public void setNum2(int num2) {
this.num2 = num2;
}
/*提供get/set方法之后其他类就可以访问这个类的私有变量了*/
}
测试类TestPrivate:
public class TestPrivate{
public static void main(String[] args) {
ObjectTest o = new ObjectTest();
// o.num1;//这样是无法访问其他类的私有变量的
System.out.println("Getter方法访问的num1:"+o.getNum1());
System.out.println("Getter方法访问的num2:"+o.getNum2());
o.setNum1(7);
o.setNum2(6);
System.out.println("Setter方法设置的num1:"+o.getNum1());
System.out.println("Setter方法设置的num2:"+o.getNum2());
}
}
运行结果:
Getter方法访问的num1:15
Getter方法访问的num2:35
Setter方法设置的num1:7
Setter方法设置的num2:6
直接调取私有变量会失败,只能通过get方法来调用,可以保证封装的数据的安全.
继承
从已有的类中吸收成员变量和方法,新的类型中可以添加新的方法和成员变量.这种方式可以提高代码复用性,缩短开发周期,减少开发费用.
计算机语言中,先编写父类,再编写子类,然后再有对象.
子类也叫派生类,subCLass .父类也叫超类,基类, superClass
继承必需的关键字:extends:
用于继承语法.格式: public class subCLass extends superClass{}
所有的类都默认继承自Object类,但是一般不写extens Object.
继承中的构造器:
子类不能继承父类的构造器,但是子类中的构造器可以调用父类的构造器.
语法:super(有参传实参);
子类中的构造器一定会有1个调用了父类的构造器.
父类中如果没有无参构造器,子类需要显式调用父类构造器,如果父类中有无参构造器,子类中的构造器可能隐式调用了父类的无参构造器super().即隐藏了super().
super();与this();的区别与相同点
相同点:都是调用构造器,且必须放在首行首句.
区别:super();是调用父类的构造器,this();是调用本类中其他构造器.
继承的传递性
继承特征有传递特性.
如b extends a, c extends a,表明c间接继承了a.
JAVA只支持单继承,即extends之后只能有一个类,可以继承一个已经是子类的父类,且一个父类可以有多个子类
编译期绑定:编译过程中变量只能调出本类中的方法.
运行期绑定:运行过程中真正执行的方法的逻辑与对象的类型有关
成员变量都与变量的类型有关,与编译期运行期无关
简单地说,方法在运行期间动态绑定到对象上,
方法在编译期间方法静态绑定到变量上.
下面给一个实例来理解继承:
父类SuperClass:
public class SuperClass {
int x;
int y;
public SuperClass(int x,int y){
this.x=x;
this.y=y;
}
public void sum(int x,int y){
System.out.println("父类的sum方法:"+(x+y));
}
public String toString() {
return "父类重写的Object类的toString:x"+x+",y:"+y+".";
}
}
子类SubClass:
public class SubClass extends SuperClass{
int z;
public SubClass(int x,int y,int z){
super(x,y);
this.z=5;//直接赋值.
}
@Override
public void sum(int x, int y) {
System.out.println("子类重写的sum方法:"+(x+y));
}
public String toString() {
return "子类重写的toString:x"+x+",y:"+y+",z:"+z+".";
}
public static void main(String[] args) {
SubClass sub=new SubClass(1,2,3);
SuperClass sup=new SubClass(4,5,6);
System.out.println(sub);
System.out.println(sup);
sub.sum(15,9);
sup.sum(18,10);
}
}
运行结果:
子类重写的toString:x1,y:2,z:5.
子类重写的toString:x4,y:5,z:5.
子类重写的sum方法:24
子类重写的sum方法:28
方法重写:@Override
子类可以继承父类的方法.在继承时,我们可以在子类中编写与父类方法名,参数列表也相同的方法(如toString方法.).
父子类关系,方法名相同,参数列表(形参类型顺序)相同.
返回值类型可以相同,也可以不同.
不同时,子类方法返回值的类型必须是父类方法的返回值类型的子类
修饰词可以不变,或者比父类的修饰权限(访问范围)更大.
如父类使用了protected,则子类只能使用public和protected.而不能使用默认与private.
父类型的变量可以引用子类型的对象.
与方法重载(@Overload)的区别
方法重载:同一个类中,方法名相同,参数列表不同.
方法重写:子类重写父类方法,方法名相同,参数列表相同,返回值类型可以相同,也 可以说父类方法返回值类型的子类.修饰词可以相同,或者修饰权限 比父类的大,父类的私有方法不能重写.
Object类
Object类是所有Java类的祖先。每个类都使用 Object 作为父类。所有对象(包括数组)都实现Object类的方法,如常用的toString方法与equals方法。
toString方法:
先看看Object类中的toString方法:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
返回类全名@HashCode值,即对象在内存中的地址信息,在 输出变量或引用变量拼接时默认调用.我们不需要这些信息,所以我们一般都重写toString()方法,用于查看对象的详情.
我们在使用System.out.println()的时候就默认调用了toString方法.
equals方法:
先看看Object类中的equals方法:
public boolean equals(Object obj) {
return (this == obj);
}
逻辑是比较调用者this与形参obj的地址信息是否相等,即比较this与obj是不是同一个对象.我们在使用时一般都需要进行重写.
重写规则:
1.查看传进来的obj是不是null.
if(obj==null) return false;
2.查看传进来的obj是不是this.
if(obj==this) return true;
3.查看传进来的obj是不是本类型.
if(obj.getClass()!=this.getClass())
return false;
可以改为:if(!(obj instanceof 类型))
return false;
instanceof关键字
作用:判断引用变量指向的对象是否属于某一类型.
语法:boolean f=变量名 instanceof 类型名
下面给出一则重写equals方法和toString方法的的实例:
public class ObjectTest {
int x;
int y;
String str;
public ObjectTest(int x,int y,String str){
this.x=x;
this.y=y;
this.str=str;
}
@Override//方法重写的标志,可写可不写
public boolean equals(Object obj) {
if (obj==null)//查看是否为空
return false;
if (this==obj)//查看是否为同一个对象
return true;
if (!(obj instanceof ObjectTest))//看看obj是不是本类型
return false;
ObjectTest o=(ObjectTest)obj;//将obj强转为本类型
if (o.x==this.x&&o.y==this.y&&o.str.equals(this.str))
//String类型是对象,所以要使用.equals来比对.
return true;
return false;
}
@Override
public String toString() {//重写toString
return "("+x+","+y+","+str+")";
}
public static void main(String[] args) {
ObjectTest o1=new ObjectTest(5,5,"o1");
ObjectTest o2=new ObjectTest(9,7,"o2");
ObjectTest o3=new ObjectTest(5,5,"o1");
System.out.println("测试重写toString之输出o1:"+o1.toString());
System.out.println("测试重写toString之输出o2:"+o2.toString());
System.out.println("测试重写toString之输出o3:"+o3.toString());
System.out.println("测试重写equals方法之比对o2与o1:"+o2.equals(o1));
System.out.println("测试重写equals方法之比对o3与o1:"+o3.equals(o1));
}
}