目录
一、封装性
封装性在Java当中的体现:
1. 方法就是一种封装
2. 关键字private也是一种封装(私有化,类似于)
使用private方法的时候,被封装后的成员变量不能直接访问,需要定义一对Getter/Setter方法,(必须叫setXxx或者getXxx命名规则)。代码如下:
public class Person {
String name;
private int age;
public void show() {
System.out.println("姓名:" + name + "年龄:" + age);
}
// 这个成员方法,专门用于向age设置数据
public void setAge(int num){
if (num < 150 && num>0){
age = num;
}else {
System.out.println("格式输入不对!");
}
}
// 这个成员方法,专门用于获取age的数据
public int getAge() {
return age;
}
}
当然,在本类中通过this关键字可以访问到本类私有化的成员变量,格式为this.成员变量(类似python中的self),当需要调用成员变量的时候,都推荐使用this.成员变量,可以将成员变量和方法变量区分开来。面向对象的思想包括高内聚低耦合,良好的封装能够减少程序的耦合度,保证属性的操作安全性。
二、继承性
继承的概念:多个类中存在相同的属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只需要继承那个类即可。
这个单独的类被称为父类,也可以叫做超类,基类等。从这个类继承的类可以称为子类。子类拥有父类的所有属性与方法,无需重新定义。并且可以直接使用非私有的父类成员,同时子类还可以定义自己的属性和方法。在继承中,可以有多个继承关系,“子类就是一个父类”。也就是说,子类可以被当作父类看待,子类有父类的特征。
在java中通过extends关键字让类与类之间产生继承关系。定义一个继承关系的类为:
首先创建父类:
定义父类的格式:(一个普通的类定义)
public class 父类名称{
//...
}
然后创建子类继承父类:
定义子类的格式:
public class 子类名称 extends 父类名称{
//...
}
2.1 继承中的注意事项
- Java只支持单继承,不支持多继承。即只能有一个父类。
- 子类可以被当作父类看待,子类有父类的特征
- 所有类均有父类,只有Object类没有父类。Object为最高类
2.2 继承设计原则
对于已经投入使用的类,尽量不要进行修改。推荐定义一个新的类,来重复利用其中共性内容,并且添加改动新内容。
2.3 继承使用详解
2.3.1 子类访问成员变量和方法
局部变量:直接写成员变量名
本类的成员变量:this.成员变量名
父类的成员变量名:super.成员变量名
父类:
public class Fu(){
Stirng name;
int age;
public void printFu(){
System.ou.println(“父类方法”)
}
}
子类:
public class Zi extends Fu(){
Stirng sex;
int id;
public void printZi(int num){ // num为局部变量
this.id += num; // 调用本类的成员变量
System.ou.println(this.id)
System.ou.println(super.name) //调用父类的成员变量
}
}
2.3.2 super关键字
继承关系中,父子类构造方法的调用特点:
1. 子类构造方法当中有一个默认隐含的“super()”调用,所以一定是先调用的父类构造,后执行的子类构造。
2. 子类构造可以通过super关键字来调用父类重载构造。
3. super的父类构造调用,必须是子类构造方法的第一个语句,不能一个子类构造调用多次super构造
总结:
子类必须调用父类构造方法,不写则赠送super();写了则用写的指定的super调用,super只能有一个,还必须是第一个。
父类:
public class Fu {
private int num = 10;
private String sex;
public Fu() {
System.out.println ("父类无参构造方法");
}
public Fu(String sex) {
this.sex = sex; // 把局部变量赋值给成员变量
System.out.println ("我是" + sex);
}
}
子类:
public class Zi extends Fu{
int num = 20;
public Zi(){
super(); // 调用父类的无参构造方法
System.out.println ("子类构造方法");
}
public Zi(String sex){
super(sex); // 调用父类的有参构造方法
}
2.3.3 this关键字
super 关键字用来访问父类内容,而this关键字用来访问本类内容。用法也有三种;
1. 在本类的成员方法中,访问本类的成员变量。
2. 在本类的成员方法中,访问本类的另一个成员方法。
3. 在本类的构造方法中,访问另一个构造方法(访问的本类独有的构造方法)。
在第3种用法当中要注意:
this(...)调用也必须是构造方法的第一个语句。
public class Zi extends Fu{
int num = 20;
public Zi(){
super();
}
public Zi(int num){
this();
}
public Zi(int num, int num2){
this(1000);
}
}
2.3.4 方法重写
1. 重写(override)
概念:发生在继承关系当中,方法的名称一样,参数列表也一样。
2. 重写和重载的区别
重写(override):方法的名称一样,参数列表也一样。
重载(overload):方法的名称一样,参数列表不一样。
3. 方法的覆盖重写特点:创建的是子类对象,则优先使用子类方法。
4. 方法覆盖重写的注意事项:
1) 必须保证父子类之间方法的名称相同,参数列表也相同。
@override:写在方法前面,用来检测是不是有效的正确覆盖重写。
这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。
2) 子类方法的返回值必须【小于等于】父类方法的返回值范围
小扩展提示:java.lang.Object类是所有类的公共最高父类(祖宗类),java.lang.String就是Object的子类。
3) 子类方法的权限必须【大于等于】父类方法的权限修饰符/
小扩展提示:public > protected > (default) > private
备注:(default)不是关键字default,而是什么都不写,留空
三、多态性
3.1 接口
在学习多态性之前,需要先了解什么是接口。
接口就是一种公共的规范标准,只要符合规范标准,就可以通用。接口是一种引用数据类型,最重要的内容就是其中的:抽象方法。
如何定义一个接口的格式:
public interface 接口名称 {
// 接口名称
}
备注:换成了关键字interface之后,编译生成的字节码文件仍然是:.java --> .class
如果是java 7,那么接口中可以包含的内容有:
3.1.2 常量
成员变量其实是常量,格式:
public static final 数据类型 常量名称 = 数据值;
注意:
- 常量必须进行赋值,而且一旦复制不能改变,因为使用了final关键字
- 常量名称完全大写,用下划线进行分隔。
- 接口当中的常量,可以省略public static final,注意:不写也照样是这样。
- 接口当中的常量,必须进行赋值,不能不赋值。
- 接口当中的常量的名称,使用完全大写的字母,用下划线进行分割
示例代码:
public interface MyInterfaceConstant {
public static final int NUM_OF_MY_CLASS = 77;
}
3.1.2 抽象方法
示例代码:
public interface MyInterfaceAbstract {
// 这是一个抽象方法
public abstract void methodAbs1();
// 这是一个抽象方法
public void methodAbs2();
}
如果是java 8,可以额外包含有:
3.1.3 默认方法
1. 格式:
public default 返回值类型 方法名称(参数列表) {
方法体
}
2. 备注:接口当中的默认方法,可以解决接口升级的问题。
示例代码:
public interface MyInterfaceDefault {
// 抽象方法
public abstract void methodAbs1();
// 添加一个默认方法
public default void methodDefault(){
System.out.println ("新添加的默认方法");
}
}
实现类可以重写默认方法,也可以不重写,都可以调用默认方法
3.1.4 静态方法
1. 格式:
public static 返回值类型 方法名称(参数列表){
方法体
}
3. 提示:就是将abstract或者default换成static即可,带上方法体
4. 注意事项:不能通过接口实现类的对象来调用接口当中的静态方法。
5. 正确用法:通过接口名称,直接调用其中的静态方法。
格式:接口名称.静态方法名(参数)
示例代码:
public interface MyInterfaceStatic {
public static void methodStatic(){
System.out.println ("接口静态方法!");
}
}
如果是java 9,还可以额外包含有:
3.1.5 私有方法
1. 普通私有方法,解决多个默认方法之间重复代码问题
格式:
private 返回值类型 方法名称(参数列表){
方法体
}
示例代码:
public interface MyInterfacePrivateDefault {
public default void methodDefault1(){
System.out.println ("默认方法1");
methodCommon ();
}
public default void methodDefault2(){
System.out.println ("默认方法2");
methodCommon ();
}
private void methodCommon(){
System.out.println ("AAA");
System.out.println ("BBB");
System.out.println ("CCC");
}
}
2. 静态私有方法,解决多个静态方法之间重复代码问题
格式:
private static 返回值类型 方法名称(参数列表){
方法体
}
示例代码:
public interface MyInterfacePrivateStatic {
public static void methodStatic1(){
System.out.println ("静态方法1");
methodStaticCommon ();
}
public static void methodStatic2(){
System.out.println ("静态方法2");
methodStaticCommon ();
}
private static void methodStaticCommon(){
System.out.println ("AAA");
System.out.println ("BBB");
System.out.println ("CCC");
}
}
3.1.6 接口实现步骤
1. 接口不能直接使用,必须有一个“实现类”,来“实现”接口
格式:
public class 实现类名称 implements 接口名称 {
// ...
}
2. 接口的实现类必须覆盖重写(实现接口中所有的抽象方法)。
实现:去掉abstract关键字,加上方法体大括号
3.创建实现类的对象,进行使用
注意事项:
如果实现类没有覆盖重写接口中的所有抽象方法,那么这个实现类自己就必须是抽象类
4. 使用接口的时候需要注意:
- 接口是没有静态代码块和构造方法的;
- 一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。
格式:
public class MyInterfaceImpl implements MyInterfaceA,MyInterfaceB {
// 覆盖重写所有抽象方法
}
- 如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可。
- 如果实现类没有覆盖重写所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类。
- 如果实现类所实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写。
- 一个类如果直接父类当中的方法,和接口当中的默认方法产生了冲突,优先使用父类当中的方法。
3.1.7 类和接口之间关系:
1. 类与类之间是单继承的,直接父类只有一个。
2. 类与接口是多实现的,一个类可以实现多个接口。
3. 接口与接口之间是多继承的。
注意事项:
1. 多个父接口当中的抽象方法如果重复,没有关系。
2. 多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,【而且带着default关键字】
3.2 多态性
多态是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行不同操作,如图所示:
3.2.1 多态的优点:
1. 消除类型之间的耦合关系
2. 可替换性
3. 可扩充性
4. 接口性
5. 灵活性
6. 简化性
3.2.2 多态存在的三个必要条件
1. 继承
2. 重写
3. 父类引用指向子类对象
java中实现多态性:
extends和implements就是多态性的前提。
3.2.3. Java中多态实现
代码当中实现多态性,其实就是一句话:父类引用指向子类对象。
格式:
父类名称 对象名 = new 子类名称();
或者:
接口名称 对象名 = new 实现类名称();
已图中为例,示例代码如下:
首先定义一个电脑接口:
public interface Computer {
void playGame(); // 定义玩游戏的抽象方法,public abstract可以省略不写
}
然后定义一个笔记本电脑的实现类:
public class Laptop implements Computer {
@Override
public void playGame() {
System.out.println ("笔记本玩游戏方便");
}
public void myLaptop() {
System.out.println ("笔记本携带方便");
}
}
台式电脑的抽象方法:
public class Desktop implements Computer{
@Override
public void playGame() {
System.out.println ("台式电脑玩游戏舒服");
}
public void myDesktop(){
System.out.println ("台式携带太麻烦啦");
}
}
3.2.4 多态的使用规则
在多态的代码当中,成员方法的访问规则是:
看new的是谁,就优先用谁,没有则向上找。
成员变量:编译看左边,运行还看左边
成员方法:编译看左边,运行看右边,在调用方法的时候,如果子类重写了父类的成员方法,
那么会优先使用子类重写的方法,但是不能直接调用子类中特有的方法。
总结:不能直接访问子类(实现类)里接口没有的成员变量或者方法。
3.2.5 向上转型和向下转型:
1. 对象的向上转型,其实就是多态写法:
格式:
父类名称 对象名 = new 子类名称();
含义:
右侧创建一个子类对象,把它当作父类来看待使用。
注意事项:
向上转型一定是安全的,没有问题的,正确的,但是也有一个弊端:
对象一旦向上转型父类,那么就无法调用子类特有的内容。
2. 对象的向下转型,其实就是一个【还原的动作】
格式:
子类名称 对象名 = (子类名称)父类对象;
含义:将父类对象,【还原】成为本来的子类对象
注意事项:
* 必须保证对象本来创建的时候,就是猫,才能向下转型成为猫
* 如果对象创建的时候本来不是猫,现在非要向下转型成为猫,就会报错。
如何才知道一个父类引用的对象本来是什么子类?
格式:
对象名 instanceof 类名称
这将会得到一个boolean值结果,也就是判断前面的对象能不能当作后面类型的实例。
代码示例:
public class DemoMain {
public static void main(String[] args) {
// // 对象的向上转型,就是:父类引用指向子类对象
Computer computer1 = new Laptop ();
computer1.playGame (); // 父子都有的方法,那么就会优先有子类重写过后的方法
//computer1.myLaptop(); // 错误写法
// 如果希望调用子类特有的方法,需要向下转型,进行“还原”
// 判断以下父类引用Computer本来是不是Laptop
if (computer1 instanceof Laptop) {
Laptop laptop = (Laptop) computer1;
laptop.myLaptop ();
}
givemeAComputer (computer1);
}
// 最好创建一个方法来判断,因为子类可能不止有一个
public static void givemeAComputer(Computer computer) {
if (computer instanceof Laptop) {
Laptop laptop = (Laptop) computer;
laptop.myLaptop ();
}else if (computer instanceof Desktop) {
Desktop desktop = (Desktop) computer;
desktop.myDesktop();
}
}
}