一、封装
定义
隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别; 将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作 数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。
为什么要封装
隐藏对象的属性和实现细节,私有属性。
封装的实现
1、修改属性的可见性来限制对属性的访问(private)
2、为每个属性创建一对赋值(setter)方法和取值(getter) 方法,用于对这些属性的访问
3、在setter和getter方法中,加入对属性的存取限制
/*例题
编写一个类Book,代表教材:
具有属性:名称(title)、页数(pageNum),其中页数不能少于200页,否则输出错误信息,并赋予默认值200
为各属性设置赋值和取值方法
具有方法:toString,用来在控制台输出每本教材的名称和页数
*/
public class Book {
private String title;
private int pageNum;
//
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
if(pageNum<200){
System.out.println("页数不能少于200页");
this.pageNum=200;
return;
}
this.pageNum = pageNum;
}
//输出对象时 调用这个方法 toString
public String toString(){
return "title" +title +" "+ "pageNum"+pageNum;
}
}
public class Teacher3 {
private String name; // 教员姓名
private int age; //年龄
public int getAge() {
return age;
}
public void setAge(int age) {
if (age<22) {
System.out.println("错误!最小年龄应为22岁!");
this.age = 22; //如果不符合年龄要求,则赋予默认值
} else {
this.age = age;
}
}
//此处省略对name属性的setter、getter方法
}
public class Teacher3Test {
public static void main(String[ ] args) {
Teacher3 teacher = new Teacher3();
teacher.setName ("李芳");
teacher.setAge(10);
System.out.println(teacher.introduction());
}
}
测试代码
public class Test {
public void bookTest() {
Book book = new Book();
book.setTitle("西游记");
book.setPageNum(300);
System.out.println(book);
}
public static void main(String[] args) {
Test test = new Test();
test.bookTest();
}
}
封装的优点
使用封装,增加了数据访问限制,增强了程序的可维护性
二、继承
定义
继承:就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接 访问父类中的非私有的属性和行为。
继承的优点
-
提高代码的复用性。
-
类与类之间产生了关系,是多态的前提
继承的格式
通过 extends 关键字,可以声明一个子类继承另外一个父类,定义格式如下:
public class 父类 {
...
}
public class 子类 extends 父类 {
...
}
继承代码演示:
/*
定义乐器类Instrument,做为父类
*/
public class Instrument {
public void play(){
System.out.println("开始弹奏");
}
}
/** 定义钢琴类Piano 继承乐器类Instrument */
//重写play方法
public class Piano extends Instrument{
public void play(){
System.out.println("我是钢琴,当当当------");
}
}
/** 定义测试类 */
public class TestPlay {
public static void main(String[] args) {
Instrument b= new Piano();
b.play();
Instrument c= new Violin();
c.play();
}
}
继承后的特点--->成员变量
*成员变量不重名
如果子类父类中出现不重名的成员变量,这时的访问是没有影响的。代码如下:
public class Demo1 {
static class Fu {
// Fu中的成员变量。
int num = 6;
}
static class Zi extends Fu {
// Zi中的成员变量
int num2 = 7;
// Zi中的成员方法
public void show() {
// 访问父类中的num,
System.out.println("输出父类中的值="+num); // 继承而来,所以直接访问。
// 访问子类中的num2
System.out.println("输出子类中的值="+num2);
}
}
static class ExtendsDemo01{
public static void main(String[] args) {
// 创建子类对象
Zi z = new Zi();
// 调用子类中的show方法
z.show();
}
}
// 演示结果:
// Fu num = 6
// Zi num2 = 7
}
**成员变量重名
如果子类父类中出现重名的成员变量,这时的访问是有影响的。代码如下:
重名情况下,只输出子类中的结果
class Fu {
// Fu中的成员变量。
int num = 5;
}
class Zi extends Fu {
// Zi中的成员变量
int num = 6;
public void show() {
// 访问父类中的num
System.out.println("Fu num=" + num);
// 访问子类中的num
System.out.println("Zi num=" + num);
}
}
class ExtendsDemo02 {
public static void main(String[] args) {
// 创建子类对象
Zi z = new Zi();
// 调用子类中的show方法
z.show();
}
}
演示结果:
Fu num = 6
Zi num = 6
子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用 super 关键字,修饰 父类成员变量。子类方法需要修改,代码如下:
class Zi extends Fu {
// Zi中的成员变量
int num = 6;
public void show() {
//访问父类中的num
System.out.println("Fu num=" + super.num);
//访问子类中的num
System.out.println("Zi num=" + this.num);
}
}
演示结果:
Fu num = 5
Zi num = 6
继承后的特点--->成员方法
a 成员方法不重名
如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有对 应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。代码如下:
class Fu{
public void show(){
System.out.println("Fu类中的show方法执行");
}
}
class Zi extends Fu{
public void show2(){
System.out.println("Zi类中的show2方法执行");
}
}
public class ExtendsDemo04{
public static void main(String[] args) {
Zi z = new Zi();
//子类中没有show方法,但是可以找到父类方法去执行
z.show();
z.show2();
}
}
b 成员方法重名——重写
如果子类父类中出现重名的成员方法,这时的访问是一种特殊情况,叫做方法重写 (Override)。
方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。
注意!
1.必须保证父子类方法名相同,参数列表也相同
2.子类方法的返回值类型必须小于等于父类返回值类型的范围
3.可以通过 @Override 注解判断是否覆盖重写
4.子类方法的权限必须 大于等于 父类的权限修饰符
public > protected > defalut(啥都不写) > private
代码如下:
class Animal {
public void move() {
System.out.println("动物可以移动");
}
}
class Dog extends Animal {
//对父类方法进行了重写
public void move() {
System.out.println("狗可以跑和走");
}
}
public class Demo2 {
public static void main(String args[]) {
Animal a = new Animal(); // Animal 对象
Animal b = new Dog(); // Dog 对象
a.move();// 执行 Animal 类的方法
b.move();//执行 Dog 类的方法
}
}
继承的特点
1、java 只支持单继承;
//一个类只能有一个父类,不可以有多个父类。
class C extends A{} //ok
class C extends A,B... //error
2、可以多层继承。
顶层父类是Object类。所有的类默认继承Object,作为父类。
class Cat extends Tiger{};
class Tiger extends Animal{}; //猫也间接继承了动物
3、继承父类只能继承父类的公共成员(公共的成员变量和成员方法)。
三、多态
定义
多态: 是指同一行为,具有多个不同表现形式。
多态存在的三个必要条件
一、要有继承;(extends)
二、要有重写;
三、父类引用指向子类对象。
多态的主要表现形式
方法的重载和方法的重写
方法的重载
一个类中,多个方法之间方法名相同,参数列表不同(个数、类型、顺序)
public class DemoA {
public void a(){
System.out.println("我是没有参数的方法");
}
public void a(int a){
System.out.println("我是有一个int参数的方法");
}
public void a(String a){
System.out.println("我是有一个String参数的方法");
}
public void a(int a,String b){
System.out.println("我是有int,String参数的方法");
}
public void a(String a,int b){
System.out.println("我是有String,int参数的方法");
}
}
方法的重写
子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写,子类重写父类的方法体。
public class DemoB {
public void a(){
System.out.println("我是父类的方法a");
}
public void b(){
System.out.println("我是父类的方法b");
}
}
public class DemoC extends DemoB{
//方法的重写
public void a(){
System.out.println("父类的方法不合适,我给改写了");
}
}
多态的实现
多态体现的格式:
父类类型:变量名=new 子类对象;
变量名.方法名();
父类类型:指子类对象继承的父类类型,或者实现的父接口类型。
多态的转型分为向上转型与向下转型两种:
向上转型: 多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。
/*
在多态的代码当中,成员方法的访问规则是:
看new的是谁,就优先用谁,没有则向上找。
口诀:编译看左边,运行看右边。
对比一下:
成员变量:编译看左边,运行还看左边。
成员方法:编译看左边,运行看右边。
*/
public static void main(String[] args) {
Fu obj = new Zi(); // 多态
obj.method(); // 父子都有,优先用子
obj.methodFu(); // 子类没有,父类有,向上找到父类
// 编译看左边,左边是Fu,Fu当中没有methodZi方法,所以编译报错。
}
}