目录
阅前说明
本文根据个人理解以及学习记录撰写,如有不对或者理解不到之处请指正。
面向对象思想
面向对象思想概述
在谈类概念前,首先要说的是Java的特点——面向对象。对象泛指某种事物,这种事物具备属性和行为。而面向对象思想是一种程序设计思想,开发者在这种思想的指引下,使用Java来设计和开发计算机程序。它强调的是通过调用对象的行为来实现功能,而不是自己去逐步完成。
以生活中的事情举例,洗衣服是我们要完成的程序,某某洗衣机是对象,它的属性可以是它的品牌,类型(滚筒还是立桶)等,功能则是洗衣。如果失去对象,完成洗衣服这个程序就需要我们用手一步步完成。
类和对象
什么是类?
类是一组相关属性和行为的集合,类似于模板。它具有的属性和行为可以用来描述一类事物。比如,洗衣机是一个类,这三个字可以描述市面上所有的洗衣机,其中包括竖桶、滚筒、带烘干、不带烘干等等各种各样品牌的洗衣机。类是一类事物的描述,是抽象的。
什么是对象?
对象是某一类事物的具体表现。它是一个类的实例,必然具备这个类的属性和行为。比如,狸花猫是猫的这个类的实例,它具备所有猫的属性和行为。对象是一类的事物的实例,是具体的。
由此可见,类是对象的模板,对象是类的实体
代码示例
public class Student {
String name;//姓名
int age;//年龄
public void study(Student student) {
System.out.println(name + "今年" + age + "岁,现在正在学习。");
}
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public class DemoStudent {
public static void main(String[] args) {
Student stu1 = new Student();//无参构造
Student stu2 = new Student("老八", 8);//全参构
stu2.study(stu2);
}
}
运行结果:
老八今年8岁,现在正在学习
这里写了两段代码,分别为类和对象。其中student类的属性为name和age,方法是study。然后对象设置为某一个具体学生,并执行类中的方法。
对象内存图
在运行main方法前,方法区首先写入数据,里面保存的是.class相关数据。由图左边可以看出,一共有两个.class。一个叫phone.class,里面包含成员变量和成员方法,另一个是main方法的.class(图片显示不完整)。
在方法区写入数据之后,从main方法开始执行。所以,main方法进入栈空间(进栈或者叫压栈),开始执行第一条语句。第一条语句创建了一个对象,等号左边相当于一个局部变量,用来保存内容,而右边则是一个对象,此时在堆内存中开辟一块空间,这块空间的内容在方法区可以找到,包括了成员方法和成员变量。成员变量区域首先保存的是变量的默认值(不同类型的默认值不同),成员方法区域保存的则是方法的地址值。回到栈中,main方法中的成员变量p保存的是堆中对象的地址值。
接着执行下一条语句p.方法名()。首先根据p可以得到对象的地址值,然后找到对象,接着根据对象保存的地址值找到方法区中的成员方法xxx。想要运行这个成员方法,也需要进栈,在栈中开辟一个新空间。方法运行完成后再执行出栈操作从栈中消失。
最后main方法再次出现,执行下一条语句重复上述过程。(图片来自网络)
继承
概述
什么是继承?
继承的概念出现在当多个类拥有相同的属性时,将这些共同的内容提取到一个单独的类中,那么这些拥有相同属性的类只需要继承这个单独的类就无需再定义这些相同的属性和行为。被继承的类称为父类,继承父类的类称为子类。
例如:动物作为一个单独类具有吃和睡这两种方法,那么凡是具有动物概念的生物都具有吃和睡这两种方法。
定义
继承:就是子类继承父类的属性和行为,使子类对象具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。
优点
- 提高代码的复用性
- 类与类之间产生了关系,是多态的前提。
格式
class 父类 { ... }
class 子类 extends 父类 { ... }
代码示例
/*
* 定义员工类Employee,做为父类
* */
class Employee {
String name; // 定义name属性
// 定义员工的工作方法
public void work() {
System.out.println("正在尽心尽力地工作");
}
}
/*
* 定义讲师类Teacher 继承 员工类Employee
*/
class Teacher extends Employee {
// 定义一个打印name的方法
public void printName() {
System.out.println("我是" + name);
}
}
/*
* 定义测试类
*/
public class ExtendDemo01 {
public static void main(String[] args) {
// 创建一个讲师类对象
Teacher t = new Teacher();
// 为该员工类的name属性进行赋值
t.name = "小明";
// 调用该员工的printName()方法
t.printName(); // name = 小明
// 调用Teacher类继承来的work()方法
t.work(); // 尽心尽力地工作
}
}
运行结果:
我是小明
正在尽心尽力地工作
上述代码可以看到,子类Teacher在继承父类Employee后享有其属性和方法,不需要单独创建就能使用。
当子类创建了相同的属性后,程序会优先使用子类中的变量。如果想要使用父类中的变量需要使用super关键字。
格式: super.父类变量名
同时,如果子类中出现和父类一样的成员方法,则子类中的方法会覆盖父类的方法,称为方法重写(Override)。如果想使用父类的成员方法同样需要使用super关键字。
格式: super.父类方法名
注意:
- 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
- 子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。
补充: 一个子类只能有一个父类。
super和this
super和this的含义
- super:代表父类空间的存储空间标识(可以理解为父类的引用)
- this:代表当前对象的引用(是本类,可以理解为谁调用就是谁)
用法
- 访问成员:
this.成员变量 ‐‐ 本类的
super.成员变量 ‐‐ 父类的
this.成员方法名( ) ‐‐ 本类的
super.成员方法名( ) ‐‐ 父类的 - 访问构造方法
this( ) ‐‐ 本类的构造方法
super( ) ‐‐ 父类的构造方法
代码示例
/*
* 定义员工类Employee,做为父类
* */
public class Employee {
String name; // 定义name属性
public Employee() {
System.out.println("父类无参构造方法被使用");
}
public Employee(String name) {
this();
this.name = name;
}
// 定义员工的工作方法
public void work() {
System.out.println("正在尽心尽力地工作");
}
}
/*
* 定义讲师类Teacher 继承 员工类Employee
*/
class Teacher extends Employee {
String nicheng;//假设昵称不是共有属性
public Teacher() {
super();
}
public Teacher(String nicheng) {
super(nicheng);
this.nicheng = nicheng;
}
// 定义一个打印name的方法
public void printName() {
System.out.println("我是" + name);
super.work();
}
}
/*
* 测试用例
* */
public class Dmo {
public static void main(String[] args)
System.out.println("----创建一个子类空参对象----");
Teacher t = new Teacher();//使用super()调用父类无参构造方法
System.out.println("----创建一个子类有参对象----");
Teacher t1 = new Teacher("小明");//使用super(参数)调用父类有参构造方法
t1.printName();//使用了super.方法名调用父类的方法
System.out.println("----创建一个父类有参对象----");
Employee e = new Employee("小李");//使用this()调用本类的无参构造方法
}
}
运行结果:
----创建一个子类空参对象----
父类无参构造方法被使用
----创建一个子类有参对象----
父类无参构造方法被使用
我是小明
正在尽心尽力地工作
----创建一个父类有参对象----
父类无参构造方法被使
抽象类
概述
当子类重写并且不再使用父类中的方法时,那么父类中的方法只需要保留方法声明,删去方法主体。没有了方法体的方法被称为抽象方法。Java语法中规定,包含抽象方法的类称为抽象类。
由此可见,抽象方法就是没有方法体的方法;抽象类就是包含抽象方法的类。
抽象类的使用格式
抽象方法
使用 abstract 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
定义格式:
修饰符 abstract 返回值类型 方法名 (参数列表);
代码举例:
public abstract void run();
抽象类
如果一个类包含抽象方法,那么该类必须是抽象类。
定义格式
abstract class 类名字 { }
代码举例
public abstract class Animal { public abstract void run(); }
代码示例
如果创建了抽象类作为父类,那么继承抽象类的子类需要重写父类的所有抽象方法,否则,该子类也必须声明为抽象类,直到有子类实现父类的抽象方法。
示例:
/*
* 父类抽象类
* */
public abstract class Animal {
public abstract void run();
}
/*
* 父类抽象类
* */
public abstract class Animal {
public abstract void run();
}
/*
* 测试用例
* */
public class Demo {
public static void main(String[] args) {
Cat cat = new Cat();
cat.run();
}
}
运行结果:
奔涌吧,后猫
此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法
注意事项
关于抽象类的使用,以下为语法上要注意的细节,虽然条目较多,但若理解了抽象的本质,无需死记硬背。
- 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
- 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的
理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
- 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设 计。
- 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象 类
理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有 意义
综合示例
要求:
群主发普通红包。某群有多名成员,群主给成员发普通红包。普通红包的规则:
- 群主的一笔金额,从群主余额中扣除,分成n等份,让成员领取。
- 成员领取红包后,保存到成员余额中。
请根据描述,完成案例中所有类的定义以及指定类之间的继承关系,并完成发红包的操作。
/*
定义父类抽象类
*/
public class User {
private String name;//身份
private double leftMoney;//余额
public User() {
}
public User(String name, double leftMoney) {
this.name = name;
this.leftMoney = leftMoney;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getLeftMoney() {
return leftMoney;
}
public void setLeftMoney(double leftMoney) {
this.leftMoney = leftMoney;
}
public void show(){
System.out.println("姓名:" + getName() + ",余额" + getLeftMoney());
}
}
/*
群主类
*/
public class Manager extends User {
public Manager() {
//本类构造方法
}
public Manager(String name, double leftMoney) {
//调用父类方法
super(name, leftMoney);
}
public ArrayList<Double> send(double num, int count) {
//创建一个存放每个红包的数组
ArrayList<Double> list = new ArrayList<>();
//判断红包金额是否大于余额
if (num > super.getLeftMoney()) {
System.out.println("金额不合法");
return list;
}
super.setLeftMoney(getLeftMoney() - num);
//创建随机红包金额
Random rd = new Random();
double n = 0;//用于存放随机金额
for (int i = 0; i < count - 1; i++) {
n = Math.random()*num;//范围在0~1.0之间 防止出现0
n = Double.parseDouble(String.format("%.2f",n));//强转
list.add(n);
num = num - n;
}
num = Double.parseDouble(String.format("%.2f",num));//强转
list.add(num);//存入最后的数字
return list;
}
}
/*
群员类
*/
public class Member extends User {
public Member() {
}
public Member(String name, double leftMoney) {
super(name, leftMoney);
}
public void open(ArrayList<Double> list, String name) {
//创建随机对象
Random rd = new Random();
// Double money = list.remove(rd.nextInt(list.size()));
Double money = list.remove(0);
System.out.println("恭喜" + name + "获得了" + money + "元")
super.setLeftMoney(getLeftMoney() + money);
}
}
/*
红包实现
*/
public class TestHongbao {
public static void main(String[] args) {
//设置群主名称和余额
Manager m = new Manager("one",100);
//塞红包
ArrayList<Double> money = m.send(80,3);
//设置群员名称和余额
Member me1 = new Member("two",50);
Member me2 = new Member("three",50);
Member me3 = new Member("four",50);
//开红包
me1.open(money,me1.getName());
me2.open(money,me2.getName());
me3.open(money,me3.getName());
//显示余额
m.show();
me1.show();
me2.show();
me3.show();
}
}
运行结果:
恭喜two获得了3.17元
恭喜three获得了25.63元
恭喜four获得了51.2元
姓名:one,余额20.0
姓名:two,余额53.17
姓名:three,余额75.63
姓名:four,余额101.2