在面向对象程式设计方法中,封装是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
封装的优点
public class classStudent {
private String name; //声明私有成员变量name
private int age; //声明私有成员变量age
}
在这段代码中,将 name 和 age 属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。
public class classStudent{
//声明私有成员变量
private String name;
private int age;
//创建公有方法,提供外部对类中私有成员变量赋值和取值
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
注意:采用 this 关键字是为了解决实例变量(private String name)和局部变量(setName(String name)中的name变量)之间发生的同名的冲突。
接下来我们来写一个实例进行具体分析
public class Student{
private String name;
private String idNum;
private int age;
public int getAge(){
return age;
}
public String getName(){
return name;
}
public void setAge( int newAge){
age = newAge;
}
public void setName(String newName){
name = newName;
}
}
以上实例中,public方法是外部类访问该类私有成员变量的入口。
这些包含"set" "get"的赋值取值方法被称为变量的setter和getter方法
因此,任何要访问类中私有成员变量的类都要通过这些getter和setter方法。
通过如下的例子说明Student类的变量怎样被访问:
public class Test {
public static void main(String[] args){
Student person=new Student();
person.setName("陈银禧");
person.setAge(18);
System.out.println("Name : "+person.getName());
System.out.println("Age : "+person.getAge());
}
}
上面的代码运行的结果为:
Name: 陈银禧
Age: 18
继承是指在现有类的基础上派生出新的类,新类将拥有现有类的属性和行为特征,并且在新类中还可以再增加新的属性和行为特征,这就是程序设计中的继承
生活中的继承:
兔子和羊属于食草动物类,狮子和豹属于食肉动物类。
食草动物和食肉动物又是属于动物类。
所以继承需要符合的关系是:is-a,父类更通用,子类更具体。
虽然食草动物和食肉动物都是属于动物,但是两者的属性和行为上有差别,所以子类会具有父类的一般特性也会具有自身的特性。
在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:
类的继承格式
class 父类 {
}
class 子类 extends 父类 {
}
为什么需要继承?
接下来我们通过实例来说明这个需求。
开发动物类,其中动物分别为企鹅以及老鼠,要求如下:
猪:属性(姓名,id),方法(吃,自我介绍)
老鼠:属性(姓名,id),方法(吃,自我介绍)
企鹅类:
public class Pig {
private String name;
private int id;
public Pig(String myName, int myid) {
name = myName;
id = myid;
}
public void eat(){
System.out.println(name+"正在吃");
}
public void introduction() {
System.out.println("大家好!我是" + id + "号" + name + ".");
}
}
老鼠类:
public class Mouse {
private String name;
private int id;
public Mouse(String myName, int myid) {
name = myName;
id = myid;
}
public void eat(){
System.out.println(name+"正在吃");
}
public void introduction() {
System.out.println("大家好!我是" + id + "号" + name + ".");
}
}
分析这两段代码可以看出来,代码存在重复了,导致后果就是代码量大且臃肿,并且维护性不高(维护性主要是后期需要修改的时候,就需要修改很多的代码,容易出错),为了从根本上解决这两段代码的问题,就需要继承,将两段代码中相同的部分提取出来组成 一个父类:
公共父类:
public class Animal {
private String name;
private int id;
public Animal(String myName, int myid) {
name = myName;
id = myid;
}
public void eat(){
System.out.println(name+"正在吃");
}
public void sleep(){
System.out.println(name+"正在睡");
}
public void introduction() {
System.out.println("大家好!我是" + id + "号" + name + ".");
}
}
这个Animal类就可以作为一个父类,然后猪和老鼠类继承这个类之后,就具有父类当中的属性和方法,子类就不会存在重复的代码,维护性也提高,代码也更加简洁,提高代码的复用性(复用性主要是可以多次使用,不用再多次写同样的代码) 继承之后的代码:
猪类:
public class Pig extends Animal {
public Penguin(String myName, int myid) {
super(myName, myid);
}
}
老鼠类:
public class Mouse extends Animal {
public Mouse(String myName, int myid) {
super(myName, myid);
}
}
继承类型
需要注意的是 Java 不支持多继承,但支持多重继承。
继承的特性
子类拥有父类非 private 的属性、方法。
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
子类可以用自己的方式实现父类的方法。
继承关键字
继承可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang 包中,所以不需要 import)祖先类。
**extends关键字 **
在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。
extends 关键字
public class Animal {
private String name;
private int id;
public Animal(String myName, String myid) {
//初始化属性值
}
public void eat() { //吃东西方法的具体实现 }
public void sleep() { //睡觉方法的具体实现 }
}
public class Penguin extends Animal{
}
implements关键字
使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。
implements 关键字
public interface A {
public void eat();
public void sleep();
}
public interface B {
public void show();
}
public class C implements A,B {
}
**super 与 this 关键字 **
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
this关键字:指向自己的引用。
实例
class Animal {
void eat() {
System.out.println("animal : eat");
}
}
class Dog extends Animal {
void eat() {
System.out.println("dog : eat");
}
void eatTest() {
this.eat(); // this 调用自己的方法
super.eat(); // super 调用父类方法
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal();
a.eat();
Dog d = new Dog();
d.eatTest();
}
}
输出结果为:
animal : eat
dog : eat
animal : eat
final关键字
final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写:
声明类:
final class 类名 {//类体}
声明方法:
修饰符(public/private/default/protected) final 返回值类型 方法名(){//方法体}
注:实例变量也可以被定义为 final,被定义为 final 的变量不能被修改。被声明为 final 类的方法自动地声明为 final,但是实例变量并不是 final
构造器
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。
实例
class SuperClass {
private int n;
SuperClass(){
System.out.println("SuperClass()");
}
SuperClass(int n) {
System.out.println("SuperClass(int n)");
this.n = n;
}
}
// SubClass 类继承
class SubClass extends SuperClass{
private int n;
SubClass(){ // 自动调用父类的无参数构造器
System.out.println("SubClass");
}
public SubClass(int n){
super(300); // 调用父类中带有参数的构造器
System.out.println("SubClass(int n):"+n);
this.n = n;
}
}
// SubClass2 类继承
class SubClass2 extends SuperClass{
private int n;
SubClass2(){
super(300); // 调用父类中带有参数的构造器
System.out.println("SubClass2");
}
public SubClass2(int n){ // 自动调用父类的无参数构造器
System.out.println("SubClass2(int n):"+n);
this.n = n;
}
}
public class TestSuperSub{
public static void main (String args[]){
System.out.println("------SubClass 类继承------");
SubClass sc1 = new SubClass();
SubClass sc2 = new SubClass(100);
System.out.println("------SubClass2 类继承------");
SubClass2 sc3 = new SubClass2();
SubClass2 sc4 = new SubClass2(200);
}
}
输出结果为:
------SubClass 类继承------
SuperClass()
SubClass
SuperClass(int n)
SubClass(int n):100
------SubClass2 类继承------
SuperClass(int n)
SubClass2
SuperClass()
SubClass2(int n):200
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作,如图所示:
多态性是对象多种表现形式的体现。
多态的优点
多态存在的三个必要条件
继承
重写
父类引用指向子类对象
比如:
Parent p = new Children();
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
以下是一个多态实例的演示:
public class Test { //声明一个测试类
public static void main(String[] args) {
show(new Cat()); // 以 Cat 对象调用 show 方法
show(new Dog()); // 以 Dog 对象调用 show 方法
Animal a = new Cat(); // 向上转型
a.eat(); // 调用的是 Cat 的 eat
Cat c = (Cat)a; // 向下转型
c.work(); // 调用的是 Cat 的 work
}
public static void show(Animal a) {
a.eat();
// 类型判断
if (a instanceof Cat) { // 猫做的事情
Cat c = (Cat)a;
c.work();
} else if (a instanceof Dog) { // 狗做的事情
Dog c = (Dog)a;
c.work();
}
}
}
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void work() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void work() {
System.out.println("看家");
}
}
执行以上程序,输出结果为:
吃鱼
抓老鼠
吃骨头
看家
吃鱼
抓老鼠
**
**
虚函数的存在是为了多态。
Java 中其实没有虚函数的概念,它的普通函数就相当于 C++ 的虚函数,动态绑定是Java的默认行为。如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数。
重写
我们将介绍在 Java 中,当设计类时,被重写的方法的行为怎样影响多态性。
当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。
要想调用父类中被重写的方法,则必须使用关键字 super。
下面我们举例来说明:
public class Employee {
private String name;
private String address;
private int number;
public Employee(String name, String address, int number) {
System.out.println("Employee 构造函数");
this.name = name;
this.address = address;
this.number = number;
}
public void mailCheck() {
System.out.println("邮寄支票给: " + this.name
+ " " + this.address);
}
public String toString() {
return name + " " + address + " " + number;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
public void setAddress(String newAddress) {
address = newAddress;
}
public int getNumber() {
return number;
}
}
假设下面的类继承Employee类:
public class Salary extends Employee //声明Salary继承Employee
{
private double salary; // 全年工资
public Salary(String name, String address, int number, double salary) {
super(name, address, number); //指定要访问的父类中的成员变量
setSalary(salary);
}
public void mailCheck() {
System.out.println("Salary 类的 mailCheck 方法 ");
System.out.println("邮寄支票给:" + getName()
+ " ,工资为:" + salary);
}
public double getSalary() {
return salary;
}
public void setSalary(double newSalary) {
if(newSalary >= 0.0) {
salary = newSalary;
}
}
public double computePay() {
System.out.println("计算工资,付给:" + getName());
return salary/52;
}
}
public class VirtualDemo { //声明一个测试类
public static void main(String [] args) {
Salary s = new Salary("员工 lina", "深圳", 3, 6000.00); //实例化一个员工对象s
Employee e = new Salary("员工 salary", "广州", 2, 8000.00);
//实例化一个对象e
System.out.println("使用 Salary 的引用调用 mailCheck -- ");
s.mailCheck(); //调用mailCheck方法
System.out.println("\n使用 Employee 的引用调用 mailCheck--");
e.mailCheck();
}
}
以上实例编译运行结果如下:
Employee 构造函数
Employee 构造函数
使用 Salary 的引用调用 mailCheck –
Salary 类的 mailCheck 方法
邮寄支票给:员工lina ,工资为:6000.0
使用 Employee 的引用调用 mailCheck–
Salary 类的 mailCheck 方法
邮寄支票给:员工salary ,工资为:8000.0
以上整个过程被称为虚拟方法调用,该方法被称为虚拟方法。
今天就介绍到这里了。若有不当请指正。