Java——面向对象
1、继承性
1.1、为什么要有继承性
为描述和处理 个人 信息,定义类 Person:
package com.atguigu.java;
public class Person {
String name;
int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("吃饭");
}
public void sleep() {
System.out.println("睡觉");
}
}
为描述和处理 学生 信息,定义类 Student:
package com.atguigu.java;
public class Student extends Person{
// String name;
// int age;
String major;
public Student() {
}
public Student(String name, int age, String major) {
this.name = name;
this.age = age;
this.major = major;
}
// public void eat() {
// System.out.println("吃饭");
// }
// public void sleep() {
// System.out.println("睡觉");
// }
public void study() {
System.out.println("学习");
}
}
package com.atguigu.java;
/*
* 面向对象的特征之二:继承性 why?
* 一、继承性的好处
* 1、减少了代码的冗余,提高了代码的复用性。
* 2、便于功能的扩展。
* 3、为之后的多态性的使用提供了前提。
*
* 二、继承性的格式 class A extends B{}
* 1、A:子类、派生类、subclass
* 2、B:父类、超类、基类、superclass
* 3、体现:一旦子类A继承了父类B以后,子类A中就获取了父类B中声明的结构,属性、方法。
* 特别的,父类中声明为private的属性和方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只是因为封装性的影响,使得子类不能直接调用父类的结构。
* 4、子类继承父类以后,还可以声明自己特有的属性或方法,实现功能的扩展。
* 5、子类和父类的关系,不同于集合和子集的关系。
* 6、extends:扩展。
*
* 三、Java中关于继承性的规定
* 1、一个类可以被多个子类继承;
* 2、一个类只能有一个父类:java中类的单继承;
* 3、子父类是相对的概念。
* 4、子类直接继承的父类,称为直接父类。
* 5、子类继承父类以后,就获取类直接父类以及所有间接父类声明的属性和方法。
*
* 四、
* 1、如果我们没有显式的声明一个类的父类的话,则此类继承与java.lang.Object类。
* 2、所有的类(除 java.lang.Object)都直接或间接继承于java.lang.Object;
* 3、意味着,所有的java类都具有java.lang.Object类声明的功能。
*/
public class ExtendsTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.age = 1;
p1.eat();
Student s1 = new Student();
s1.eat();
s1.sleep();
s1.name = "Tom";
}
}
为什么要有继承?
- 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
1.2、定义和特征
- 1、子类继承了父类,就继承了父类的方法和属性。
- 2、在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。
- 3、在 Java 中,继承的关键字用的是“ extends ””,即子类不是父类的子集而是对父类的“扩展”
- 4、继承的类称为 子类 (派生类), 被继承的类称为 父类 (基 类)或 超类 。 可以理解为 ::“子类 is a 父类”
类继承语法规则
class Subclass extends SuperClass{}
作用:
-
1、继承 的 出现减少了代码冗余,提高 了代码的复用性。
-
2、继承的出现,更有利于功能的扩展。
-
3、继承 的出现让类与类之间产生了 关系 ,提供了多态的 前提 。
-
4、注意:不要 仅为了获取其他类中某个功能而去继承
关于继承的规则: -
1、子类不能直接访问父类中私有的 ( private)的成员变量和方法。
-
2、Java 只 支持 单 继承和多层继承 不允许多重继承
- 一个子类只能有一个父类
- 一个父类可以派生出多个子类
class SubDemo extends Demo{ }//ok
class SubDemo extends Demo1,Demo2... //error
1.3、方法的重写 (override/overwrite)
定义 :在子类中可以根据需要对从父类中继承来的方法进行改造 也称为方法 的 重置、覆盖 。在程序执行时,子类的方法将覆盖父类的方法。
方法的声明: 权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{
//方法体
}
约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
要求
-
1.子类重写的方法 必须 和父类被重写的方法 具有相同的 方法名称、 参数 列表
-
2.子类重写的方法的返回值类型
不能大于
父类被重写的方法的返回值类型 -
3.子类重写的方法使用的访问权限
不能小于
父类被重写的方法的访问权限- 子类不能重写父类中声明为 private 权限的方法
-
4、返回值类型:
- 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
- 父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double)
- 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
-
5.子 类方法抛出的异常
不能大于
父类被重写方法 的异常
注意:
子类与父类中同名同参数的方法必须同时声明为非 static 的 即为 重写 )),或者同时声明 为static 的 不是 重写 。因为 static 方法是属于类的,子类无法覆盖父类的方法。
package com.atguigu.java;
public class Person {
String name;
int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println("吃饭");
}
public void walk(int distance) {
System.out.println("走路" + distance);
}
public Object info() {
return null;
}
}
package com.atguigu.java;
public class Student extends Person{
String major;
public Student() {
}
public Student(String major) {
this.major = major;
}
public void study() {
System.out.println("学习" + major);
}
public void eat() {
System.out.println("学生吃饭");
}
public String info() {
return null;
}
}
package com.atguigu.java;
/*
* 方法的重写(override/overwrite)
* 1、子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作。
*
* 2、重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
*
* 3、重写的规定:
* 方法的声明:权限修饰符 返回值类型 方法名(形参列表) throw 异常的类型{方法体}
* 约定俗成:子类中的叫重写的方法;父类中的叫被重写的方法。
* (1)子类重写的方法名和形参列表与父类被重写的方法名和形参列表相同。
* (2)子类重写的方法的权限修饰符不小于父类被重写的权限修饰符。
* (3)子类不能重写父类中声明为private的方法。
* (4)返回值类型:
* >父类被重写的方法的返回值类型,则子类重写的方法的返回值类型也只能是void。
* >父类被重写的方法的返回值类型是A类型,则子类重写的返回值类型可以是A类或A类的子类。
* >父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须是相同的基本数据类型。
* (5)子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型。
*
* 子类和父类中的同名同参数的方法要么都声明为非static(重写),要么都声明为static(不叫重写)。
* 区分方法的重载与重写:
*
*/
public class PersonTest {
public static void main(String[] args) {
Student s = new Student("计算机");
s.eat();
s.walk(10);
s.study();
Person p1 = new Person();
p1.eat();
}
}
1.4、四 种访问权限修饰符
实例:
1、同一个包下
Order类
package com.atguigu.java2;
/*
* 体会4种不同的权限修饰
*
*
*/
public class Order {
private int orderPrivate;
int orderDefault;
protected int orderProtected;
public int orderPublic;
private void methodPrivate(){
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
void methodDefault(){
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
protected void methodProtected(){
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
public void methodPublic(){
orderPrivate = 1;
orderDefault = 2;
orderProtected = 3;
orderPublic = 4;
}
}
OrderTest 类
package com.atguigu.java2;
public class OrderTest {
public static void main(String[] args) {
Order order = new Order();
order.orderDefault = 1;
order.orderProtected = 2;
order.orderPublic = 3;
order.methodDefault();
order.methodProtected();
order.methodPublic();
//同一个包中的其他类,不可以调用Order类中私有的属性、方法
// order.orderPrivate = 4;
// order.methodPrivate();
}
}
2、不在同一个包下
OrderTest 类
package com.atguigu.java3;
import com.atguigu.java2.Order;
public class OrderTest {
public static void main(String[] args) {
Order order = new Order();
order.orderPublic = 1;
order.methodPublic();
//不同包下的普通类(非子类)要使用Order类,不可以调用声明为private、缺省、protected权限的属性、方法
// order.orderPrivate = 2;
// order.orderDefault = 3;
// order.orderProtected = 4;
//
// order.methodPrivate();
// order.methodDefault();
// order.methodProtected();
}
public void show(Order order){
order.orderPublic = 1;
order.methodPublic();
//不同包下的普通类(非子类)要使用Order类,不可以调用声明为private、缺省、protected权限的属性、方法
// order.orderPrivate = 2;
// order.orderDefault = 3;
// order.orderProtected = 4;
//
// order.methodPrivate();
// order.methodDefault();
// order.methodProtected();
//
}
}
1.5、关键字 super
在 Java 类中使用 super 来调用父类中的指定操作:
- super 可用于访问父类中定义的属性
- super 可用于调用父类中定义的成员方法
- super 可用于在子 类 构造 器 中 调用父类的 构造 器
注意:
- 尤其 当子父类出现同名成员时 可以 用 super 表明调用的是父类中的成员
- super 的追溯不仅限于直接父 类
- super 和 this 的用法相像 ,this 代表本类对象的引用, super 代表父类的内存空间 的标识
调用父类的构造器
- 子类中所有的 构造 器 默认 都会访问父类中 空参数 的 构造 器
- 当父类中没有空参数的构造器时 子类的构造器必须通过 this( 参数列表) 或者 super( 参数列表)
语句指定调用本类或者父类中相应的构造 器 。 同时 只能二选一
且 必须 放在构造 器 的 首 行 - 如果子类构造器中既未显式调用父类或本类的构造器 且父类中又没有无参的构造器 则 编译出错
package com.atguigu.java;
public class Person {
String name;
int age;
int id = 1001; //身份证号
public Person(){
System.out.println("无处不在");
}
public Person(String name){
this.name = name;
}
public Person(String name, int age){
this(name);
this.age = age;
}
public void eat(){
System.out.println("人,吃饭");
}
public void walk(){
System.out.println("人,走路");
}
}
package com.atguigu.java;
public class Student extends Person {
String major;
int id = 1002; //学号,属性不覆盖
public Student(){
}
public Student(String major){
this.major = major;
}
public Student(String name, int age, String major){
super(name, age);
this.major = major;
}
public void eat(){
System.out.println("Student吃");
}
public void study(){
System.out.println("学生,学习知识");
super.eat();
}
public void show(){
System.out.println("name = " + this.name + ", age = " + this.age);
System.out.println("id = " + super.id);
}
}
package com.atguigu.java;
/*
super关键字的使用
1、super理解为:父类的
2、super可以用来调用:属性、方法、构造器。
3、super属性和方法
(1)我们可以在子类的方法或构造器中,通过使用"super.属性"或"super.方法"的方式显式的调用父类中声明的属性或方法。但是通常情况下,我们都习惯了省略"super."。
(2)特殊情况:当子类和父类中定义了同名的属性时候,我们要想在子类中调用父类中声明的属性,则必须的显式的使用"super.属性"的方法,表明调用的是父类中声明的属性。
(3)特殊情况:当子类重写了父类的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
4、super调用构造器
(1)我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定构造器。
(2)"super(形参列表)"的使用,必须声明在子类构造器的首行。
(3)我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现。
(4)在构造器的首行,没有显式声明"this(形参列表)"或"super(形参列表)",则默认调用的是父类中空参的构造器"super()";
(5)在类的多个构造器中,至少有一个类的构造器中使用了"super(形参列表)",调用了父类的构造器。
*/
public class SuperTest {
public static void main(String[] args){
Student s = new Student();
s.show();
s.study();
Student s1= new Student("Tom", 21, "IT");
s1.show();
}
}
如果子类构造器中既未显式调用父类或本类的构造器 且父类中又没有无参的构造器 则 编译出错
this和 super 的区别
**子类对象实例化的全过程**
-
1、从结果上来看(继承性):子类继承父类以后,就获取了父类中声明的属性或方法。创建子类的对象,在堆空间中,就会加载所有父类的属性和方法。
-
2、从过程上来看:当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器…直到调用了java.lang.Object类中的空参构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类的结构,子类对象才可以考虑进行调用。
-
明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
、
2、多态性
2.1、多态性
多态性,是面向对象中最重要的概念 在 Java 中 的 体现:对象的 多态性:父类的引用指向子类的对象,可以 直接应用在抽象类和 接口上
Java 引用变量有两个类型: 编译时类型 和 运行时类型 。 编译时类型由声明该变量时使用的类型决定 运行时类型由实际赋给该变量的对象 决定 。 简称: 编译 时 看左边;运行时 看右边 。
若编译时类型和运行时类型不一致 就出现了对象的多态性
多态情况下 :
- (1)看左边 看的是父类的引用 (父类中不具备子类特有的方法)
- (2)看右边 看的是子类的对象 (实际运行的是子类重写父类的方法)
注意:
-
对象的多态: 在 Java 中 子类 的对象可以替代 父类 的对象使用
- 一个变量只能有一种确定的数据类型
- 一个引用类型变量可能指向 引用 多种不同类型的对象
-
子类可看做是特殊的父类 所以父类类型的引用可以指向子类的对象:向上转型 upcasting 。
-
一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就 不能 再访问子类中添加的属性和方法
虚拟方法调用(Virtual Method Invocation) -
正常的方法调用
-
虚拟方法调用
多态情况 下子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
- 编译时类型和运行时类型
编译时e 为 Person 类型,而方法的调用是在运行时确定的,所以调用的是 Student 类的 getInfo 方法——动态绑定
小结:方法的重载与重写
2.2、instance of操作符
x instanceof A
:检验 x 是否为类 A 的对象,返回值为 boolean 型。
- 要求 x 所属的类与类 A 必须是子类和父类的关系,否则编译错误。
- 如果 x 属于类 A 的子类 B, x instanceof A 值也为 true 。
package com.atguigu.exer;
public class InstanceTest {
public static void main(String[] args){
InstanceTest test = new InstanceTest();
test.method(new Person());
}
public void method(Person e){
//虚拟方法的的重载
String info = e.getInfo();
System.out.println(info);
if(e instanceof Graduate){
System.out.println("Graduate");
}else if(e instanceof Student){
System.out.println("Student");
}else{
System.out.println("Person");
}
}
}
class Person{
protected String name="Person";
protected int age = 50;
public String getInfo(){
return "Name:" + name + "\n" + "age:" + age;
}
}
class Student extends Person{
protected String school = "pku";
public String getInfo(){
return "Name:" + name +"\n" + "age:" + age + "\nschool:" + school;
}
}
class Graduate extends Student{
public String major = "IT";
public String getInfo(){
return "Name:" + name +"\nage:" + age + "\nschool:" + school +"\nmajor" + major;
}
}
2.3、对象类型转换(Casting)
基本数据类型的 Casting
自动类型转换 :小的数据类型可以自动转换成大的数据类型
long g= 20 double d= 12.0f
- 强制类型转换: 可以把大的数据类型强制转换 ( 成小的数据类型)
float f=(float) 12.0 , int a=( int )1200 L
对 Java 对象的强制类型转换称为造型
- 从子类到父类的类型转换可以自动进行
- 从父类到子类的类型转换必须通过造型 强制类型转换 实现
- 无继承关系的引用类型间的转换是非法的
- 在造型前可以使用 instanceof 操作符测试一个对象的类型
2.4、Object类的使用
Object 类是所有 Java 类的根父类
如果在类的声明中未使用 extends 关键字指明其父类 则默认父 类为 java.lang.Object 类
Object类中 的主要结构
2.4.1、==操作符与 equals 方法
==操作符与 equals 方法对比
==操作符
- 基本 类型 比较值: 只要两个变量的值相等 即 为 true 。
- 引用类型比较引用 是否指向同一个 对象 )):只有 指向同一个对象时 才返回 true 。
- 用
==
进行比较时 符号两边的 数据类型必须兼容 (可自动转换的基本数据类型除外 )否则 编译 出错
equals 方法
package com.atguigu.java;
/*
* 面试题:==和equals的区别?
* 一、回顾==的使用
* ==:运算符
* 1、可以使用在基本数据类型变量和引用数据类型变量中;
* 2、如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等(不一定类型要相同,自动类型提升,不与boolean玩)
* 3、如果比较的是引用类型变量:比较的是两个对象的值是否相同。即两个引用是否指向同一个对象实体。
*
* 二、equals方法的使用
* 1、是一个方法,而非运算符
* 2、只能适用于引用数据类型
* 3、Object类中中equals()方法的定义:
* public boolean equals(Object obj){
* return (this == obj);
* }
* 说明:Object类中定义的equals()和==的作用是相同的。
* 4、像String、Date、File、包装类都重写了Object类中的equals方法,重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的实体内容是否相同。
5.通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们就需要对Object类中的equals方法进行重写。
重写的原则:比较两个对象的实体内容是否相同。
补充:==符号使用时,必须保证符号左右两边的变量类型一致。
*/
public class EqualsTest {
String name;
int age;
public boolean equals(Object obj){
if(this == obj){
return true;
}
if(obj instanceof EqualsTest){
EqualsTest cust = (EqualsTest)obj;
// if(this.age == cust.age && this.name.equals(((EqualsTest) obj).name)){
// return true;
// }else{
// return false;
// }
return (this.age == cust.age && this.name.equals(((EqualsTest) obj).name));
}
return false;
}
}
2.4.2、toString() 方法
2.5、包装类 (Wrapper) 的使用
针对 八 种 基本数据类型定义 相应的引用类型 包装类(封装类)