封装
概念
- 将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的访问和操作。
作用
- 直接通过操控类对象,不需要对具体实现十分了解,使类属性和方法的具体实现对外不可见。不但方便还起到了保护作用。
其他
- 该露的露,该藏的藏
- 程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外界使用。
- 封装(数据的隐藏)
- 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口访问,这称为信息隐藏。
- 封装主要封装类的属性,一般不封装类的方法
- 封装的基本步骤:1. 修改类的属性的可见性(设为private)2. 创建getter/setter方法(用于对类中私有化的属性进行读写)3. 在getter/setter方法中加入属性控制语句(对属性值的合理性进行判断)
封装的实现----使用访问控制符
Java是使用“访问修饰符”来控制那些细节需要封装,那些细节需要暴露的。
Java中4种“访问控制符”分别为private、default(默认)、protected、public,它们说明了面向对象的封装性,所以我们要利用它们尽可能的让访问权限降到最低,从而提高安全性。
参考博文
private:表示私有,只有自己类能访问。
default:表示没有修饰符,只有同一个包的类能访问
protected:表示可以被同一个包的类以及其他包中的子类访问
public:表示可以被该项目的所有包中的所有类访问
封装使用细节
类的属性的处理:
- 一般使用private访问权限。
- 提供相应的get/set方法来访问相关属性,这些方法通常是public修饰的,以提供对属性的赋值与读取操作(注意boolean变量的get方法是is开头)。
- 一些只用于本类的辅助性方法可以用private修饰,希望其他类调用的方法用public修饰。
重点:属性私有,然后通过调用get、set方法对属性进行更改
package com.oop.demo03;
//类
public class Student {
//属性私有
private String name;//名字
private int id;//学号
private char sex;//性别
private int age;//年龄
//提供一些可以操作这个属性的方法!
//提供一public的get、set方法
//get获得这个数据
public String getName(){
return this.name;
}
//set给这个数据设置值
public void setName(String name){
this.name=name;
}
//alt+insert自动生成get、set方法
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age>120||age<0){//不合法
this.age = 3;
}else{
this.age = age;
}
}
}
package com.oop.demo03;
public class Application {
public static void main(String[] args){
Student s1 = new Student();
System.out.println(s1.getName());
s1.setName("潘潘");
System.out.println(s1.getName());
s1.setAge(-1);//不合法
System.out.println(s1.getAge());
}
}
运行结果:
null
潘潘
3
继承
参考博文
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
所有类都继承于java.lang.Object,当一个没有继承的关键字,则默认继承object祖先类。
类的继承格式
Java中通过extends关键字可以申明一个类是从另一个类继承而来的。
class 父类{
}
class 子类 extends 父类{
}
继承的类型
注意Java只有单继承,没有多继承,但支持多重继承。
继承的特性
- 父类也成为超类、基类、派生类等
- Java中只有单继承,没有像C++那样的多继承,多继承会引起混乱,使得继承链过于复杂,系统难以维护。
- Java中类没有多继承,接口有多继承。
- 子类继承父类,可以得到父类的全部属性和方法(除了父类的构造方法),但不见的可以直接访问(如父类私有的属性和方法)。
- 如果定义一个类时,没有调用extends,则它的父类是Java.lang.Object
- 子类拥有父类的非private的属性、方法。
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法,即对父类方法进行重写。
- Java的继承是单继承,但是可以多重继承。多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类。
- 提高了类之间的耦合性(继承的特点,耦合性高就会造成代码之间的联系越紧密,代码独立性越差)
继承关键字
- extends关键字
在Java中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 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具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。
public interface A {
public void eat();
public void sleep();
}
public interface B {
public void show();
}
public class C implements A,B {
}
Object类
Object类是所有Java类的根基类,也就意味着所有Java对象都拥有Object类的属性和方法。
super与this
super关键字:可以通过super关键字来访问父类中(被子类覆盖)的方法和属性,用来引用当前对象的父类。
注:子类是不继承父类的构造方法的,它只是调用(显示或隐式)。如果父类的构造器带有参数,则必须在子类的构造器中显示地通过super关键字调用父类的构造器并配以适当的参数列表。如果父类没有显示的构造方法,则在子类的构造方法中不需要使用super关键字调用父类的构造方法,系统会自动调用父类的无参构造方法。
- 使用super调用普通方法,语句没有位置限制,可以在子类中随便使用。
- 若是类的构造方法中的第一行代码没有显示的使用super(…)或this(…)调用父类或本类自身构造方法,那么Java会默认调用super(),含义是调用其父类的无参构造方法,这里super()可以省略。
- 所有类的构造方法中的第一句代码都是super()(不管显示还是隐式)来调用父类对应的构造方法。
this关键字:指向本类,用来引用当前对象。
package com.oop.demo04;
//父类
public class Person {
protected String name = "panpan";
public Person() {
System.out.println("执行Person父类无参构造方法");
}
public Person(String name) {
this.name = name;
System.out.println("执行Person父类有参构造方法");
}
public void print(){
System.out.println("Person");
}
}
package com.oop.demo04;
//子类继承了父类,就会拥有父类的全部方法
public class Student extends Person{
private String name = "ouhong";
public Student() {
//隐藏代码:隐式调用了父类的无参构造方法,super()
//若显示调用父类构造方法,必须将super(...)或super放在第一句
System.out.println("执行student子类构造方法");
}
public void print(){
System.out.println("student");
}
public void test(){
//不建议采用此格式调用本类属性或方法
System.out.println(name);//调用本类属性,与this.name效果相同
System.out.println(this.name);//调用本类属性,建议使用的格式
System.out.println(super.name);//调用父类属性
}
public void test1(){
print();//student,调用子类方法
this.print();//student,调用子类方法
super.print();//Person,调用父类方法
}
}
package com.oop;
import com.oop.demo04.Student;
public class Application {
public static void main(String[] args){
Student student = new Student();
System.out.println();
student.test();
System.out.println();
student.test1();
}
}
运行结果:
执行Person父类无参构造方法
执行student子类构造方法
ouhong
ouhong
panpan
student
student
Person
final关键字
final关键字的作用:
- 修饰变量:被他修饰的变量,一旦赋初值了,就不能再重新赋值。
final int MAX_NUM = 120;
- 修饰方法:该方法不可被子类重写,但是可以被重载。
final void study(){
}
- 修饰类:修饰的类不能被继承,比如:Math、String等。
final class A{
}
多态
多态是同一个行为具有多个不同表现形式或形态的能力。
多态的要点
- 多态是方法的多态,不是属性的多态(多态与属性无关)。
- 多态的存在要有3个必要条件:继承、方法重写、父类引用指向子类对象。
- 父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。
Parent p = new Child();
对象的转型
父类引用指向子类对象,我们称这个过程为向上转型,属于自动类型转换。
向上转型后的父类引用变量只能调用它编译类型的方法,不能调用它运行时类型的方法。若想要调用它运行时类型的方法,这时,我们就需要进行类型的强制转换,我们称之为向下转型。
package com.oop.demo05;
//重写都是方法的重写,和属性无关
public class Parent {
public void test(){
System.out.println("Parent-->test()");
}
}
package com.oop.demo05;
public class Child extends Parent{
//Override 重写
@Override//有功能的注解
public void test() {
System.out.println("Child-->test()");
}
public void eat(){
System.out.println("eat()");
}
}
package com.oop;
import com.oop.demo04.Student;
import com.oop.demo05.Child;
import com.oop.demo05.Parent;
public class Application {
public static void main(String[] args){
//Child子类能调用的方法都是自己的或者继承父类的
Child c = new Child();
c.test();
System.out.println();
//对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
//即若子类重写了父类的该方法,则调用子类的该方法;若子类没重写该方法,则调用父类的该方法。
//父类调用指向子类
Parent p = new Child();//子类重写了父类的方法,执行子类的方法
p.test();
//p.eat();//报错,Parent父类,可以指向子类,但是不能调用子类独有的方法
((Child) p).eat();//向下转型
}
}
运行结果:
Child-->test()
Child-->test()
eat()
方法的重写
- 子类通过重写父类的方法,可以用自身的行为替换父亲的行为。
- 当子类对象调用重写的方法时,调用的是子类的方法,而不是父类中被重写的方法。
- 要想调用父类中被重写的方法,则必须使用关键字 super。
重写的条件:
参考博文
方法的重写需要符合下面的三个要点:
- 重写只跟非静态方法(成员方法)有关,与静态方法无关。静态方法和非静态方法不一样,在静态方法中,方法的调用只和左边声明的对象类型有关,而与右边无关,是哪个类型,就调用对应的方法。即父类引用指向子类,无论子类中是否有与父类相同的方法,都只调用父类的方法。
- 声明为static的方法不能被重写,但是能够再次声明。
- 子类和父类在同一个包中时,子类可以重写父类除了声明为 private 和 final 方法的其他方法。
- 子类和父类不在同一个包中时,子类只能重写父类的声明为 public 和 protected 的非 final 方法。
- 方法名、形参列表必须相同,只是方法体不同。
- 返回值类型和声明异常类型范围,子类小于等于父类。
- 访问权限,子类大于等于父类,public>protected>default>private
package com.oop.demo05;
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;
}
}
package com.oop.demo05;
public class Salary extends 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;
}
}
package com.oop;
import com.oop.demo05.Employee;
import com.oop.demo05.Salary;
public class Application {
public static void main(String[] args){
Salary s = new Salary("员工 A", "北京", 3, 3600.00);
Employee e = new Salary("员工 B", "上海", 2, 2400.00);
System.out.println("使用 Salary 的引用调用 mailCheck -- ");
s.mailCheck();
System.out.println("\n使用 Employee 的引用调用 mailCheck--");
e.mailCheck();
}
}
运行结果:
Employee 构造函数
Employee 构造函数
使用 Salary 的引用调用 mailCheck --
Salary 类的 mailCheck 方法
邮寄支票给:员工 A ,工资为:3600.0
使用 Employee 的引用调用 mailCheck--
Salary 类的 mailCheck 方法
邮寄支票给:员工 B ,工资为:2400.0
例子解析:
- 实例中,实例化了两个 Salary 对象:一个使用 Salary 引用 s,另一个使用 Employee 引用 e。
- 当调用 s.mailCheck() 时,编译器在编译时会在 Salary 类中找到 mailCheck(),执行过程 JVM 就调用 Salary 类的 mailCheck()。
- e 是 Employee 的引用,但引用 e 最终运行的是 Salary 类的 mailCheck() 方法。
- 在编译的时候,编译器使用 Employee 类中的 mailCheck() 方法验证该语句, 但是在运行的时候,Java虚拟机(JVM)调用的是 Salary 类中的 mailCheck() 方法。
多态的实现方式
- 方法的重写
- 接口(还未整理)
- 抽象类和抽象方法(还未整理)