java面向对象有4大特征,按出现顺序分别是,封装、继承、抽象、和多态。本文讲述封装和继承的特性及代码演示。
封装
是指隐藏对象的不需要对外提供的属性和实现细节,仅对外提供公共访问方式。
具体封装时,可以将需要隐藏的属性和方法用private修饰,private修饰的成员变量和成员方法只在本类范围内有效,类以外即使创建了对象了也不能直接访问,因此对私有变量,需对外提供访问方法,一般是get和set函数。
对外提供的访问方式中,可以对访问数据加入逻辑判断等语句,有利于数据的安全使用,也提高了代码的健壮性。
封装不是私有,私有仅仅是封装的一种表现形式,私有是最小权限,通过其它的权限设定也可以达到封装。
这里总结下类和类中成员的修饰符:
类的修饰符:
default 同包内可在其它类中访问,同包类可在其子类中访问;
public 拥有最大权限,继承,可以其它任何地方访问;
final 被final修饰的类不能被继承。
类成员变量和成员函数的权限修饰符:
private 类访问权限:本类内部可以访问,不能继承到子类,实例对象也不能访问;
default 什么都不写,就是default权限,也叫包访问权限,本类内部可以访问,同包其他类也可以访问,同包内可继承;
protected 子类访问权限,本类内部可以访问,同包其他类也可以访问, 可继承,同包及不同包的子类都可以访问;
public 公共访问权限:任何地方都可以访问,能继承到子类。
通过下面这个例子演示类和类成员的权限问题:
package itcast.A;
public class Person{
public String name;
private int age;
protected int height;
String sex;
public Person(String name,int age,int height,String sex){
this.name=name;
this.age=age;
this.height=height;
this.sex=sex;
}
public int getAge(){ //对外提供访问私有成员age的方法
return this.age;
}
}
class Student extends Person //Student类是default权限
{
public String birthday;
private int paiming;
double score;
Student(String name,int age,int height,String sex){
super(name, age,height,sex);
}
void show(){
System.out.println(name+" "+" "+height+" "+sex);/*子类中可以访问default和protected变量,但是不能访问private变量,所以此处不能打印年龄*/
}
}
package itcast.A;
public class QXDemoA {
public static void main(String[] args) {
Person p=new Person("lisi",21,170,"male");
/*System.out.println("lisi年龄:"+p.age);编译不通过,private变量不能在对象中访问*/
System.out.println("lisi身高,性别:"+p.height+","+p.sex);/*default和protected成员可由对象访问*/
Student s=new Student("zhangsan",19,172,"female"); /*同包可以访问default权限的Student类*/
s.show();//default成员函数可以在同包其它类中访问
}
}
package itcast.B;
import itcast.A.Person;
class Worker extends Person{
public Worker(String name,int age,int height,String sex){
super(name,age,height,sex);
}
void display(){
System.out.println(name+" "+height);/*protected成员变量可以跨包在子类中访问,但是default成员不能跨包在子类中继承,此外不能打印sex*/
}
}
public class QXDemoB {
public static void main(String[] args) {
Person pp=new Person("wangwu",17,160,"male");/*Person类构造函数是public的,可以跨包访问*/
/*System.out.println(pp.name+" "+pp.sex);编译不通过,default成员变量不能跨包在其它类中访问*/
/*System.out.println(pp.name+" "+pp.height);编译不通过,protected成员变量不能跨包在非子类中访问*/
}
}
继承
类与类之间有is a的所属关系时,可以让子类继承父类;继承可以提高代码的复用性。
子类继承父类时,类中成员变量和成员函数都呈现出什么特性呢?
先上一个简单程序,继承时子父类变量的特点都体现在代码注释中了:
class Fu{
int num=4;
Fu(){
System.out.println("Fu run");
}
Fu(int num){
this.num=num;
}
}
class Zi extends Fu{
int num=6;
Zi(){
System.out.println("Zi run");
System.out.println(num);/*此处num等价于this.num;
如果将Zi类中的num注释掉,此处的num可以理解为super.num,也可以理解为this.num,
因为Zi类中继承了父类的num属性,num也是Zi类的成员; */
}
}
public class ExtendDemo1 {
public static void main(String[] args) {
Zi zi=new Zi();/* 创建Zi对象时,先加载Fu.class文件,再加载Zi.class文件;
在zi对象所在的堆内存中会给父类的属性num和子类的num都开辟空间,即使父类的num属性是private的;
实际上,此时内存中只有Zi类对象,没有Fu类对象,Zi类构造函数的super和this引用都是指向这个zi对象。
*/
}
}
该程序运行结果也说明了继承时子父类构造函数的特点,运行结果是:
Fu run
Zi run
6
可见new Zi()时,父类的构造函数Fu()也会自动执行。实际上,父类中的数据子类可以直接获取,所以子类对象在建立时,需要先查看父类中这些数据是如何进行初始化的,所以子类在对象初始化时,必须先访问一下父类的构造函数。
构造函数继承的特点:
子类的每一个构造函数内的第一行都有一句隐式super()语句;
当父类中没有空参数的构造函数时,子类中必须通过super或this语句来指定要访问的父类中的构造函数。
---之所以可以用this语句的原因:this语句代表调用子类的某个构造函数,该构造函数中如果没有this语句时肯定有super语句,这样也就访问了父类的构造函数,而子类所有构造函数中肯定有一个构造函数中没有this语句(否则成了构造函数间完全相互调用了,死循环),所以子类的每个构造函数中都会直接或间接调用父类构造函数;this语句和super语句都是放在构造函数第一行的,2者不能并存,只能2选1.
子父类函数继承的特性:覆盖(重写)
当子父类中出现一模一要函数时,当子类对象调用此函数,会运行子类函数的内容,就像父类函数被覆盖了一样,这种情况就是覆盖。
覆盖注意事项:
子类覆盖父类,必须保证子类函数权限大于父类函数权限,否则编译失败;父类的private函数对子类是不可见的,所以一般不覆盖。
静态只能覆盖静态。