面向对象之封装、继承、多态
封装
将类中的信息进行隐藏在类的内部,不允许外部程序信息直接访问,而是只能通过该类的提供的方法来实现对隐藏信息的提供和访问。
- 好处
只能隐藏规定的方法和数据
隐藏类的实现细节,方便修改和实现
封装的实现步骤
1. 用private等修饰符修饰属性
2. 创建属性的getter/setter方法
3. 在getter/setter方法中加入属性的控制语句
java中的访问修饰符
java中的访问修饰符一个有四个,我们主要学习private
、protected
、public
三个。
java中的this关键字
this关键字代表当前对象
this.属性 操作当前对象的属性
this.方法 操作当前对象的方法
封装对象的属性的时候会经常使用this关键字
package com.test.cat; // 定义包
public class Cat {
int a;
int b;
public void send() {
System.out.print("send");
}
public int getA() {
this.send(); // 调用方法
return a;
}
public void setA(int a) {
this.a = a; // 调用属性
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
public Cat() {
System.out.println("Cat 实例化了");
}
}
java中的内部类
内部类( Inner Class )就是定义在另外一个类里面的类。与之对应,包含内部类的类被称为外部类。
内部类的主要作用
1. 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类
2. 内部类的方法可以直接访问外部类的所有数据,包括私有的数据
3. 内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便
内部类的分类
成员内部类
静态内部类
方法内部类
匿名内部类
- 先举个例子
//外部类HelloWorld
public class HelloWorld {
// 内部类Inner,类Inner在类HelloWorld的内部
public class Inner {
// 内部类的方法
public void show() {
System.out.println("welcome to imooc!");
}
}
public static void main(String[] args) {
// 创建外部类对象
HelloWorld hello = new HelloWorld();
// 创建内部类对象
Inner i = hello.new Inner();
// 调用内部类对象的方法
i.show();
}
}
java中的成员内部类(普通内部类)
package innerClass;
// 外部类
public class Outer {
private int out; // 外部类成员
private int b; // 外部的b
public int getOut() {
return out;
}
public void setOut(int out) {
this.out = out;
}
// 外部类构造函数
public Outer(int _out) {
this.out = _out;
System.out.println("外部类实例化中.....");
// 外部类则无法访问内部类的数据
}
// 内部类
public class Inner{
private int in; // 内部类成员
private int b; // 内部的b
// 内部类构造函数
public Inner(int _in) {
this.in = _in;
System.out.println("内部类实例化中....");
System.out.println(out); // 内部类可以访问外部类中的任何数据
System.out.println(this.b); // 如果遇上重名,那么默认是使用内部的变量,如果想要使用外部的变量,可以使用this
}
}
public static void main(String [] args) {
Outer o = new Outer(100); // 创建一个外部对象
System.out.println(o.getOut());
Inner i = o.new Inner(200); // 创建一个内部对象
}
}
- 注意事项
1. Inner 类定义在 Outer 类的内部,相当于 Outer 类的一个成员变量的位置,Inner 类可以使用任意访问控制符,如 public 、 protected 、 private 等
2. Inner 类中定义的 test() 方法可以直接访问 Outer 类中的数据,而不受访问控制符的影响
3. 定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去 new 一个内部类对象,即:内部类 对象名 = 外部类对象.new 内部类( );
4. 编译上面的程序后,会发现产生了两个 .class 文件
其中,第二个是外部类的 .class 文件,第一个是内部类的 .class 文件,即成员内部类的 .class 文件总是这样:外部类名$内部类名.class
- 外部类是不能直接使用内部类的成员和方法滴
- 如果外部类和内部类具有相同的成员变量或方法,内部类默认访问自己的成员变量或方法,如果要访问外部类的成员变量,可以使用 this 关键字。
Java 中的静态内部类
静态内部类是 static 修饰的内部类
- 特点
1. 静态内部类不能直接访问外部类的非静态成员,但可以通过 new 外部类().成员 的方式访问
2. 如果外部类的静态成员与内部类的成员名称相同,可通过“类名.静态成员”访问外部类的静态成员;如果外部类的静态成员与内部类的成员名称不相同,则可通过“成员名”直接调用外部类的静态成员
3. 创建静态内部类的对象时,不需要外部类的对象,可以直接创建 内部类 对象名= new 内部类();
package static_inner_class;
public class Outer {
private int a; // 外部普通成员变量
static int b = 10; //外部静态变量
public Outer(int _a) {
this.a = _a;
System.out.println("外部类实例化中....");
}
// 内部类
public static class Inner{
private int b;
public Inner(int _b) {
this.b = _b;
System.out.println("内部类实例化中....");
}
public void test() {
System.out.println("访问外部类中的b:" + Outer.b);
// 访问外部静态变量,建议使用class.staticName ,即使这是处理外部类和内部类存在同名变量的情况下
System.out.println("访问内部类中的b:" + b); // 访问内部变量可以直接访问
}
}
public static void main(String [] args) {
Outer o = new Outer(10);
Inner i = new Inner(20); // 创建内部类可以直接使用该方法,不用先实例化外部类
i.test();
}
}
Java 中的方法内部类
方法内部类就是内部类定义在外部类的方法中,方法内部类只在该方法的内部可见,即只在该方法内可以使用。
//外部类
public class HelloWorld {
private String name = "爱慕课";
// 外部类中的show方法
public void show() {
// 定义方法内部类
class MInner {
int score = 83;
public int getScore() {
return score + 10;
}
}
// 创建方法内部类的对象
MInner mi=new MInner();
// 调用内部类的方法
int newScore=mi.getScore();
System.out.println("姓名:" + name + "\n加分后的成绩:" + newScore);
}
// 测试方法内部类
public static void main(String[] args) {
// 创建外部类的对象
HelloWorld mo=new HelloWorld();
// 调用外部类的方法
mo.show();
}
}
继承
继承就是类与类之间的一种
is a
的关系
java中继承是单继承的,也就是只能由一个父类
- 好处
1. 实现代码的复用
2. 子类拥有父类的所有属性和方法
3. 但是private修饰的属性和方法却无法访问到
- 语法规则
例子
// Animal.java
package inhert_demo1;
public class Animal {
public int age;
public String name;
public void eat() {
System.out.println("动物可以吃东西");
}
}
// Cat.java
package inhert_demo1;
public class Cat extends Animal{
// 【方法的重写】
public void eat() {
System.out.println("猫咪可以吃东西");
}
}
// test.java
package inhert_demo1;
public class test {
public static void main(String [] args) {
Cat c = new Cat();
c.eat();
}
}
方法的重写
如果子类对继承父类的方法不满意,是可以重写父类的方法的,当调用的时候,优先调用子类的方法
// 规定
1. 返回值相同
2. 参数类型及个数相同
3. 方法名相同
继承的出发顺序
1. 先初始化父类,然后再初始化子类
2. 先执行初始化对象中的属性,然后再执行构造函数中的初始化
// 执行顺序为
package inhert_demo1;
public class Animal {
public int age = 10; //【1】 属性初始化
public String name;
public Animal() {
age = 20; // 【2】构造函数中的初始化
}
public void eat() {
System.out.println("动物可以吃东西");
}
}
final "最终的"
final可以修饰类、属性、方法、变量
final修饰的类,则该类不能被继承
final修饰的方法,则方法不允许被覆盖(也就是方法的重写)
final修饰的属性: 属性初始化必须是在属性初始化或者构造方法中初始化,只能人选其一
final修饰的变量,这个变量就成为了常量,只能赋值一次
super
super关键字代指父类 (在对象内部使用、可以代表父类对象)
// 子类 cat
package inhert_demo1;
public class Cat extends Animal{
public int age = 20;
public void eat() {
System.out.println("猫咪可以吃东西");
}
public void meyhod () {
// super代表父类
System.out.println(super.age); // 使用父类的属性
super.eat(); // 子类中调用父类的方法
}
}
- 注意
1. 如果子类中的构造方法没有显示的调用父类的构造方法,那么系统将默认调用父类的无参构造方法
也就是会隐世的调用`super()`
2. 如果父类没有无参构造方法,而是有有参的构造方法,那么子类中就必须显示的调用父类的构造方法。
Object类
Object类是所有类的父类,如果一个类没有明确的使用
extends
来指定继承一个类,那么这个类默认继承Object(Object类中的方法适合子类)
方法一、toString()
Object中的类方法toString()返回的是一个对象的哈希Code码(对象的地址引用)
// 我们则是想使用该方法来返回属性的值,因此我们可以重写该方法
// cat类中
public String toString() {
return "Cat [age=" + age + "]";
}
//使用
package inhert_demo1;
public class test {
public static void main(String [] args) {
Cat c = new Cat();
System.out.println(c.toString());
}
}
方法二、equals()
比较对象的引用是否指向同一块内存地址。
Dog d1 = new Dog();
Dog d2 = new Dog();
d1.d2是两个地址,是不相同的
// 如果我们想要用equals来比较两个对象的值是否相同,这就需要我们重写该方法
// cat中重写
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())// getClass 获取到的是类对象
return false;
Cat other = (Cat) obj;
if (age != other.age)
return false;
return true;
}
Cat c = new Cat();
System.out.println(c.toString());
Cat c1 = new Cat();
if(c1.equals(c)) {
System.out.println("两个对象的属性相同");
}
- 补充 getClass 获取到的是类对象
多态
多态指的是对象的多种形态
1. 引用多态
父类的引用可以指向子类的对象
父类的引用可以指向本类的对象
public static void main(String [] args) {
Animal d1 = new Animal();
Animal d2 = new Dog();
Dog d3 = new Animal(); // 报错了,因为是子类的引用指向了父类的对象
}
2. 方法多态
创建本类对象时,调用的时本类的方法
创建子类对象时,调用的方法时子类重写的方法或者时父类继承过来的方法
Animal d1 = new Animal();
Animal d2 = new Dog();
d1.eat(); // 调用父类的eat方法
d2.eat(); // 调用子类重写的eat方法
多态中的引用类型转换
1. 向上类型转换(隐式/自动类型转换) 小类型 => 大类型
2. 向下类型转换(强制类型转换) 大类型 => 小类型
3. instanceof可以解决引用对象的类型,来避免转换类型的安全性问题
package polymorphic;
public class test {
public static void main(String [] args) {
Dog dog = new Dog();
Animal animal = dog; // 小类型 => 大类型
// Dog dog2 = animal; // 报错了,因为大类型 => 小类型 存在风险
Dog dog2 = (Dog)animal; // 强制类型转换,就不报错了
// Cat cat =(Cat) animal; // 编译时Cat类型,运行时时Dog类型
if(animal instanceof Dog) {
} else {
}
}
}
抽象类
使用abstract修饰的类成为抽象类
1. 某一个父类只知道子类应该含有怎样的方法,但是无法确定这个子类的方法如何实现
2. 从多个具有相同特征的类中抽象出一个抽象类,以这个抽线类作为子类的模板,从而避免了子类设计的随意性
- 使用规则
abstract定义抽象方法,只有声明、不需要实现
抽象类中可以有普通方法,也可以有抽象方法
抽象类不能直接创建,我们可以定义引用变量
public abstract class telphone {
// 定义抽象方法
public abstract void call();
public abstract void message();
// 抽象方法没有方法体,因此以分号结束
}
实例
// Telphone.java
package abstrated;
public abstract class Telphone {
// 定义抽象方法
public abstract void call();
public abstract void message();
// 抽象方法没有方法体,因此以分号结束
}
// Cellphone.java
package abstrated;
public class Celltelphone extends Telphone {
public void call() {
System.out.println("通过键盘打电话");
}
public void message() {
System.out.println("通过键盘发短信");
}
}
// smartphone.java
package abstrated;
public class Smarttelphone extends Telphone{
public void call() {
System.out.println("通过语音打电话");
}
public void message() {
System.out.println("通过语音发短信");
}
}
// initial.java
package abstrated;
public class initial {
public static void main(String[] args) {
// TODO Auto-generated method stub
Telphone t1 = new Celltelphone(); // 使用父类的引用创建子类对象
t1.call();
t1.message();
Telphone t2 = new Smarttelphone(); // 使用父类的引用创建子类对象
t2.call();
t2.message();
}
}
接口
接口可以理解为时一种特殊的类,由全局常量和公共的抽象方法来组成
它是类的一种具体实现,而接口定义了某一批类所需要遵守的【规范】,接口并不关心这些类的内部实现,也不关心这些类的实现细节,只是规定了这些类的必须实现的某些方法。
接口定义
interface A{//定义一个接口
public static final String MSG = "hello";//全局常量
public abstract void print();//抽象方法
}
// X.java
package interfaceDemo1;
interface A{
public static final String MSG = "hello"; // 全局常量
public abstract void print(); // 抽象方法
}
interface B{
public static final int a = 10;
public abstract void get();
}
public class X implements A,B{
// X实现了两个接口
public void print() {
System.out.println("接口A的抽象方法print()");
}
public void get() {
System.out.println("接口B的抽象方法get()");
}
}
// test.java
package interfaceDemo1;
public class test {
public static void main(String[] args) {
// TODO Auto-generated method stub
X x = new X(); // 实例化子类
A a = new X();
B b = new X();
x.print();
x.get();
a.print();
b.get();
}
}
未完待续