一丶继承
1. 继承的写法
在 Java 中继承的 关键字是 extends 代表一个类继承另一个类.
继承的含义以及作用: 继承就是基于某个父类的扩展. 制定出来的一个新的子类. 子类可以继承父类原有的属性跟方法. 也可以自己增加特有的属性跟方法.
代码例如下:
- public class Anmail {
- public void eat() {
- System.out.println("父类吃");
- }
- }
父类代码
子类代码:
- public class Dog extends Anmail {
- public void eat() {
- System.out.println("子类吃");
- }
- }
通过上面代码. 我们可以看到. 子类 Dog 类. 继承了父类. 使用了关键字 extends
并且子类重写了父类的方法.
2. 子类访问父类的方法
上面说了子类继承父类. 那么子类也可以调用父类的方法. 我们学过 this 关键字. this 可以区分局部变量跟成员变量. 也可以在构造中调用其它构造函数.
那么我们还提供了一个关键字. super() super 关键字可以访问父类.
代码如下:
父类代码一样. 之帖子类代码.
- public class Dog extends Anmail {
- public Dog() {
- super(); // 调用父类构造 调用构造的时候必须放在最上面.
- super.eat();// 调用父类方法.
- }
- public void eat() {
- System.out.println("子类吃");
- }
- }
创建子类对象. 并且输出.
可以看到. 在调用构造的时候. 他会先访问父类的构造. 但因为父类构造我们并没有输出内容. 所以没有输出内容, 子类继续调用父类的 eat 方法. eat 方法我们输出了.
就是父类吃. 所以在子类构造的时候. 会调用父类构造以及父类的方法.
super()的关键字用法限制. super 关键字只能调用父类中 公共权限 (public) 以及保护全选(protected) 的方法
3. 重写的概念
子类可以重写父类的方法. 什么是重写. 就是子类跟父类的方法是一模一样的. 也就是说, 重写是在子类跟父类中才会出现的. 返回值一样. 方法名一样. 参数一样.
在 J2SE 5.0 以上. 支持了新的功能. 也就是说返回值可以不一样. 但是 方法名 跟 参数必须一样.
JAVA 类编译的流程. java 中. 创建子类的时候. 会自动调用父类的构造方法进行初始化. 我们可以做个例子. 并且重写一个方法.
- public class Anmail {
- public Anmail() {
- System.out.println("父类构造方法");
- }
- public void eat() {
- System.out.println("父类吃");
- }
- }
子类.
- public class Dog extends Anmail {
- public Dog() {
- }
- public void eat() { // 重写父类方法
- System.out.println("子类吃");
- }
- }
创建对象.
通过实例可以终结出. 1. 子类重写了父类方法. 输出的内容是自己的 "子类吃" 2. 在给子类实例化的时候. 会自动调用父类进行实例化操作. 也就是说父类也会被初始化.
PS: 子类实例化的时候. 会调用父类的无参构造进行实例化父类. 但是并不会自动调用父类的有参构造. 这个我们需要使用 super 关键字才可以.
二丶 Object 类
object 类是一个比较特殊类的. 位于 java.lang. 包中. 它是所有类的父类. 比如我们以前学习过字符串类. String 类. String 类中 我们比较两个对象是否相等就是用.
equleas()方法. 这个就是 object 类中的. 只不过字符串进行了重写. 我们自定义的类也是继承自 object 类. 只不过是默认继承. 所以任何类都可以重写父类 object 中的方法.
在 object 类中 加了 final 类的方法是不能被重写的. 例如 getClass() notify() notifyAll() wait()等等.
object 类的方法介绍.
1.getClass()方法
getClass()方法会返回指定是的 Class 实例. 然后可以使用此时调用 getName()获得这个类的名称.
getClass().getName(); 也可以配合 toString()方法使用.
2.toString()方法
toString()方法就是返回一串字符串. 在 object 类中, 就是讲一个对象返回为字符串形式. 实际应用中就是重写这个字符串. 返回什么是你自定的.
3.equals()方法;
equals()方法就是比较. 当时说过区别. 就是 == 与 equals()的区别. == 是比较地址. equals()是比较你自定的内容. 也就是对象的实际内容. 通常也是重写.
比较什么你自己定. 如果你写了一个类有一个成员变量是 a; 我们重写 equals() 就判断 a 跟 比较对象的 a 即可. 就是 a 跟 a 比较.
三丶对象类型转换.
对象类型转换. 包括向上转型. 以及向下转型. 通俗理解就是 强转对应的数据类型. 但是你在强转的时候要判断一下是否是这个数据类型. 这个就是转型.
向上转型以及向下转型就是说 类我们强转为父类. 也可以父类强转为子类.
1. 向上转型
子类对象赋值给父类对象称为向上转型. Anmail a = new Dog(): 这个就是向上转型.
比如我们有动物对象. 跟 狗对象. 狗对象可以看做是动物对象的一个子类.
还比如 四边形类 跟 平行四边形类. 平行四边形 对象可以看做是 四边形类的一个对象.
如下图:
常规的继承图都是父类在上. 子类在下. 例如上图. 所以我们将子类看做是父类对象的时候成为向上转型. 也就是平行四边形对象看做是四边形类的对象的时候.
向上转型是具体的类像抽象的类进行的转换. 所以它总是安全的. 我们可以说平行四边形是特殊的四边形. 但是不能是四边形是平行四变形.
因为代码写法: 四边形 a = new 平行四变形(); 所以很多人就会说 a 就是平行四边形. 其实是错的. a 是四边形. 我们只能说 a 平行四边形是一个特殊的四边形.
如果在 C++ 中. 内存分配就是 父类占一小块内存. 子类上半部分是父类内存. 下半部分是子类特有的成员变量开辟的内存. 子类转为父类. 就是不要子类下边的内存了.
所以总是安全的. 我只要上面的哪块内存. 也就是父类的内存.
所以在上边. 子类转为父类. 父类调用方法的时候. 并不会调用到子类特有成员变量.
2. 向下转型
向下转型就是 抽象的类转为具体的类. 比如 动物是鸟. 动物是抽象的. 不能说他是鸟. 所以不和逻辑. 而且会出现问题.
比如父类 Anmail a = new Dog();
Dog b = a; 这样是错误的. 我们不能这样赋值. 原因就是不能说 动物是狗.
Dog c = (Dog)a; 这样可以. 强转为子类型. 写法是正确的.
站在 C++ 的角度:
为什么上面向下转型是错误的. 原因是 a 会有一块内存. 我们可以假定为 0x20 个字节大小. b 是 Dog 也就是子类对象. 他继承了父类. 有自己特有的成员方法
以及成员变量. 所以它的头 0x20 个字节是父类的内存. 下面多出的内存是自己了. 假设是 0x30 个字节. 所以我们子类转为父类(向上转型)
其实就是把 30 个字节的内存转为 20 个字节的内存. 所以不会出问题. 但是 0x20 个字节. 也就是父类转为子类. 就会出为题. 意思就是说 0x20 个字节转为 0x30 个字节.
首先我们并不知道是转为 0x30 个字节. 这样内存访问就会出错了. 但是如果我们强转了. 相当于就是父类在强转为子类的时候. 按照子类的内存强转. 这样就不会有问题了.
也就是上面的我们的 Dog c c 的头 0x20 个字节是父类. 下面的 0x10 个字节就是自己特有的所以不会出错.
3. 使用关键字判断对象类型
上面我们的父类转为子类的时候. 必须加上子类的数据类型才可以强转. 原因就是转为子类的时候. 内存会按照子类的大小进行扩大. 这样就不会出现问题了.
但是我们怎么知道 我们的子类. 是否是这个父类的子类. 所以有了运算符 instanceof()来判断.
就是判断 子类是否是父类的. 父类中有没有这个子类. 如果有就进行转换.
语法:
Myobject instance ExampleClass
1.Myobject 就是某个类的对象引用. 可以理解为是父类填写的是父类引用.
2.ExampleClass 某各类. 可以理解为子类. 填写类名.
例如:
- Anmail a = new Anmail();
- if (a instanceof Dog)
- {
判断父类中是否有子类 Dog, 如果有我就进行转换.
- Dog d = (Dog) a;
- d.xxxx;
- }
四丶方法重载.
1. 重载介绍.
重载的含义. 重载就是可以有多个相同函数. 重载的参数确定是否重载.
我们写过有参构造跟无参构造. 方法名是一样. 不一样的就是参数列表不同.
重载的构成:
1. 重载的构成是方法名字一样,
2. 参数列表个数不同
3. 参数类型不同
4. 参数列表顺序不同
例如:
public void eat();
public void eat(String,int); 重载 eat, 参数是 String,int
public void eat(int,String); 参数顺序不同. 构成重载.
public int eat(int,int); 返回值是 int. 参数列表是两个 int 值. 返回值也可以为 void 不影响重载.
PS: 方法的返回值并不会影响重载. 真正影响的是参数列表. 你有两个成员方法. 方法命一样. 参数列表一样. 类型一样. 返回值不同. 不能构成重载.
特殊的重载:
Java 中可以定义不定长的参数类表.
语法如下:
返回值 方法名(参数数据类型 . . . 参数名称) 主要是三个...
其实不定长参数. 就是一个一维数组. 我们可以当做数组去操作参数.
代码如下:
- public void eat(int ... a) {
- for (int i = 0; i < a.length;i++)
- {
- .......
- }
- }
5. 多态的概念
在上面我们学过向上转型. 就是子类对象可以当做父类对象去使用. 其意思就是我可以当做父类对象去使用. 那么就可以使用子类跟父类的共有的方法跟属性了.
因为我们站在内存的角度上也说了. 我们用的都是父类的内存. 所以我们可以调用父类的方法. 跟成员去使用. 但是多态是什么意思. 多态就是调用父类的方法的时候.
因为子类重写了父类的方法. 所以调用时会调用子类的特有方法.
例如:
- Anmail a = new Dog();
- a.eat()
输出结果: 子类吃. 按理说应该输出父类吃. 不是说向上转型了. 我们用的都是父类内存了. 子类就不该会被调用. 愿意你是这样了. 如果站在 C++ 角度来说.
我们首先会 new 一个子类的对象实例. 而 new 子类对象的时候. 会先初始化父类. 在初始化自己. 为什么说一下流程. 原因是父类有虚表. 也就是有一个表. 保存着
自己的方法. 而子类在实例化的时候. 父类的虚表先初始化. 初始化完了之后. 子类的再初始化. 它会先把父类的虚表拷贝过来. 然后覆盖他. 这样我们 a 调用方法的时候
其实调用的是子类方法. 原因就是子类的虚表已经覆盖了父类虚表.
Java 中的原理. java 中其实也是一样的. 只不过给你隐藏了这个步骤了. 不用理解的这么复杂. 我们只要知道. 向上转型之后. 调用子类跟父类共有的方法. 就能实现多态.
注意: 子类重写了父类方法. 那么调用的时候才是子类的方法. 原因是子类重写了. 才会覆盖父类.
代码例子:
多态的用法: 多态的好处就是程序员不同定义相同的方法了. 避免了相同的大量重复代码的开发. 只要实例化一个子类对象. 维护这个方法即可.
再举个例子;
我们手机. 有一代手机 二代手机 三代手机 四代手机 ...n 代手机.
1 代手机 只支持打电话
2 代手机 可以发信息了.
3. 手机 可以上网了
4. 手机 可以拍照了.
此时我们只需要二代手机继承 1 代手机对 1 代手机扩展功能. 比如增加发信息的方法. 打电话的方法进行重写. 可以打电话. 也可以录音了. 而一代手机根本不用动方法.
此时我们的二代手机就可以出手了. 第三代手机同样继承第二代手机. 增加扩展功能. 重写维护的方法. 而不用修改二代的代码.
这样不管我们有第几代手机. 只需要继承上一代的类. 进行扩展. 以及维护即可.
6. 抽象类
抽象类就是说一个不可以被存在的类. 比如我们有动物 跟 狗. 而动物是不能被实例化对象的. 狗是一个具体的生物. 我们可以实例化.
所以抽象就是指 动物. 也就是说我不能让你被实例化. 原因就是 动物泛指万千. 不能具体为一个动物.
定义抽象类的关键字
abstract
使用 abstract 修饰的类称为抽象类. 是不能实例化的.
使用 abstract 修饰的方法. 称为抽象方法. 是不能实现的. 比如子类重写. 修饰的方法没有方法体.
反过来说. 如果一个类中修饰了成员方法. 那么就必须定义这个类为抽象类.
抽象类跟普通类一样. 只不过就是不能实例化. 必须要有子类继承. 如果有抽象方法. 子类必须重写.
抽象类的继承图:
代码写法, 需要将我们的 Anmail 类写成抽象类. 并且方法改为抽象方法
- //public abstract class Anmail
- abstract public class Anmail {
- abstract public void eat() ;
- public abstract void play();
- }
abstract 卸载权限修饰符的前边或者后边都可以. 不影响. 例如类上面加了一行注释. 我们也可以写成上面的写法. 以及抽象方法. 也是两个不同顺序来举例子
- public class Dog extends Anmail {
- @Override
- public void eat() {
- // TODO 自动生成的方法存根
- System.out.println("狗在吃东西");
- }
- @Override
- public void play() {
- // TODO 自动生成的方法存根
- System.out.println("狗在玩耍");
- }
- }
使用的时候. 可以使用向上转型. 使用多态的方式. 子类可以自己 new 自己
在 C++ 中. 也有抽象类的概念. 只不过称之为纯虚类. 他的方式就是这个类中的方法定义为纯虚方法(抽象方法)
void eat() = 0; 后面加上 = 0; 跟接口很类似. 一般也是接口
七丶接口的含义
接口就是抽象类的延伸. 上面我们定义了一个抽象类. 如果这个抽象类是一个父类. 有很多子类. 但是我们可以这样想一下. 如果子类很多. 都要实现这个抽象类中的方法.
这样就造成了代码冗余. 我们有的类完全可以不实现抽象类中的抽象方法啊.
比如 play 方法. 我们每个子类都要实现. 但是有的动物就不会玩我完全可以不实现了. 但是按照抽象类. 我们必须实现. 所以就代码冗余了.
此时接口就出现了. 接口就是说 . 我接口中的方法都是抽象方法. 你要实现 play. 就可以实现我这个接口. 如果不需要玩的话. 就不用实现我这个接口
例如下图:
可以看到我们的 N 边型类. 并没有实现接口. 我完全可以不用实现 Draw 方法. 虽然我继承了父类.
接口的定义与使用:
1. 接口使用 intface 关键字 修饰. 可以使用权限修饰符修饰
2. 接口的实现使用关键字 implements 关键字
3. 接口中的方法都是抽象方法. 而且都没有方法体. 且默认加的关键字就是 abstract 而且权限必须是 public 因为要被实现. 其它权限修饰则不被编译器认可.
4. 在继承一个类同时. 可以实现接口.
5. 在接口中定义的 字段 (成员变量) 都是默认修饰符 static + final 修饰的. 也就是说一个静态常量.
代码如下:
定义一个接口
- public interface IAnmail {
- public abstract void Play(); // 定义一个抽象方法
- }
接口的实现.
- public class Dog extends Anmail implements IAnmail{
- @Override
- public void eat() {
- // TODO 自动生成的方法存根
- System.out.println("狗在吃东西");
- }
- @Override
- public void Play() { // 实现了接口必须实现他的方法
- // TODO 自动生成的方法存根
- System.out.println("狗在玩耍");
- }
- }
八丶总结
1. 类的继承
类的继承使用 extends 关键字
注意的问题:
1. 不能多继承.
2. 子类继承父类自动用于父类的方法以及成员变量
3. 子类在构造中可以调用父类构造.(无参或者有参) 使用的是 super(); 关键字. 也可以调用父类方法 super.xxxx
2. 公共父类 Object
在 Java 中每个类都继承了父类 Object. Object 中提供了常用的方法
比较: equeals();
转为字符串表现形式 toString();
这些方法都是可以被重写的. 除了加了 final 修饰的.
3. 对象的类型转换.
在 Java 中有向上转型跟向下转型
1. 向上转型: 子类转为父类. Anmail d = new Dog(): 安全的. 因为在内存角度来说. 使用的部分只有父类部分内存.
2. 向下转型: 父类转为子类 Dog c = (Dog) d; 必须加上类型. 父类内存转成子类内存, 比如给指定子类内存大小.
3. 对象类型判断 使用 instanceof
语法 父类引用 instance 子类类名 判断父类是否有这个子类
如果是我们就可以进行转换了.
4. 类中的方法重写与重载
重写:
1. 返回值相同
2. 方法名相同
3. 参数列表相同
满足以上三点才能是重写. 也就是子类跟父类一模一样才是重写.
重载:
1. 方法名一样
2. 参数列表个数不同
3. 参数列表类型不同
4. 参数列表顺序不同.
满足以上四点才能构成重载.
特殊重载可以使用 三个点定义为可变参数 public void eat(int ...a); 此时 a 就是一个数组. 我们可以遍历他来获取参数.
5. 多态
多态就是父类可以调用子类跟父类共有的方法. 比如是子类重写父类才会有不同的结果.
使用向上转型
Anmai a = new Dog(); a.eat() ; 狗在吃东西.
内存角度来说. 就是虚表覆盖.
6. 抽象类
抽象类使用 abstract 关键字进行修饰
public abstract class 类名{}'
1. 修饰类就是抽象类, 不可以被实例化. 就是不能创建对象
2. 修饰方法就是抽象方法, 没有方法体. 子类继承比如重写.
3. 一个类中有抽象方法. 则这个类必须定义为抽象类.
7. 接口
接口定义的关键字 Intface public intfact 接口名{}
实现接口的关键字 implements public class Anmail Implements 接口名
1. 接口中的方法都是抽象方法. 且默认修饰符为 public abstract 权限必须是 public 否则编译器不认可.
2. 接口中的成员变量都是默认的 static final 修饰的.
来源: https://www.cnblogs.com/iBinary/p/9792055.html