一、面向对象OOP
面向对象以校园管理系统为例,变量之间的关联性不明确,无法将他们作为一个整体来处理,如果学生的信息量很大,那么会出现代码冗余和难以维护的问题。
变量的内存空间随机分配,在设置变量时,他们的存储位置不相邻。
/**
* @document: 分离变量来存储学生信息
* @Author:SmallG
* @CreateTime:2023/7/25+9:19
*/
public class Demo01 {
public static void main(String[] args) {
//定义一个学生
String name1 ="tom";
int age1 = 22;
char gender1 = '男';
double score1 = 85.5;
//定义另一名学生
String name2 = "Jerry";
int age2 = 21;
char gender2 = '男';
double score2 =86.5;
//输出第一名学生的信息
System.out.println("姓名:"+name1);
System.out.println("年龄:"+age1);
System.out.println("性别:"+gender1);
System.out.println("成绩:"+score1);
//输出第二名学生的信息
System.out.println("姓名:"+name2);
System.out.println("年龄:"+age2);
System.out.println("性别:"+gender2);
System.out.println("成绩:"+score2);
}
}
面向对象概述
面向对象编程的核心思想是将数据和操作数据的方法封装到一起,形成称为对象的实体。
面向对象的三大特点:封装、继承、多态
封装:将数据和对数据的操作封装在一个单元中,只暴露必要的接口供外部访问,可以确保数据的安全性和一致性。通过类的实例化创建对象。
继承:继承允许一个类继承另一个类的属性和方法,实现代码的重用和扩展。
多态:同一个方法可以在不同的对象上具有不同的行为。多态性使得不同类型的对象可以以统一的方式进行操作,提高代码的灵活性和可扩展性。
二、类和对象
1、类
类是创建对象的模版和蓝图,它定义了对象的属性和行为。
对象是类的实例化结果,它是类的具体实体。
/**
* @document: 定义一个类,一个java程序可以定义多个类,但是只能有一个public类
* 程序的入口main方法在public声明的类中。
* @Author:SmallG
* @CreateTime:2023/7/25+10:21
*/
public class Demo02 {
public static void main(String[] args) {
}
}
//定义(声明)一个类
class Student {
//属性(变量)
String name; //名字
int age; //年龄
char gender; //性别
double score;//成绩
}
//定义教师类
class Teacher {
String name;
int age;
char gender;
double salary; //薪水
}
2、对象
创建对象的过程通常被称为“实例化”
局部变量需要初始化才能使用,类中的成员属性不需要初始化,它有默认值。
3、引用类型
引用类型是一种操作对象的数据类型,与基本数据类型相比,基本数据类型存储内容直接存储到相应位置,引用数据类型存储的是对象的引用或内存地址,也就是说引用类型开辟了两个内存空间一个存对象的首地址,一个存数据。
/**
* @document: 定义一个类,一个java程序可以定义多个类,但是只能有一个public类
* 程序的入口main方法在public声明的类中。
* @Author:SmallG
* @CreateTime:2023/7/25+10:21
*/
public class Demo02 {
public static void main(String[] args) {
//创建学生对象
Student student1 = new Student();
//为对象的属性赋值 field(属性)
student1.name = "Tom";
student1.age = 22;
student1.gender = '男';
student1.score = 85.5;
Student student2 = new Student();
student2.name = "Jerry";
student2.age = 21;
student2.score = 86.5;
student2.gender = '男';
System.out.println("学生1的信息");
System.out.println("姓名:" + student1.name);
System.out.println("性别:" + student1.gender);
System.out.println("年龄:" + student1.age);
System.out.println("成绩:" + student1.score);
System.out.println("学生2的信息");
System.out.println("姓名:" + student2.name);
System.out.println("性别:" + student2.gender);
System.out.println("年龄:" + student2.age);
System.out.println("成绩:" + student2.score);
}
}
//定义(声明)一个类
class Student {
//属性(变量)
String name; //名字
int age; //年龄
char gender; //性别
double score;
}
//定义教师类
class Teacher {
String name;
int age;
char gender;
double salary; //薪水
}
4、构造器
是一种特殊的方法,用于封装对象属性的初始化过程,也叫构造方法。构造器的名称与类名相同,并且没有返回类型声明。
public class Demo02 {
public static void main(String[] args) {
Student student1 =new Student("Tom",21,'男',85.5);
Student student2 =new Student("Jerry",19,'男',86.5);
System.out.println("学生1的信息");
System.out.println("姓名:" + student1.name);
System.out.println("性别:" + student1.gender);
System.out.println("年龄:" + student1.age);
System.out.println("成绩:" + student1.score);
System.out.println("学生2的信息");
System.out.println("姓名:" + student2.name);
System.out.println("性别:" + student2.gender);
System.out.println("年龄:" + student2.age);
System.out.println("成绩:" + student2.score);
}
}
//定义(声明)一个类
class Student {
//属性(变量)
String name; //名字
int age; //年龄
char gender; //性别
double score;
//定义一个构造方法
public Student(String name_, int age_, char gender_, double score_) {
//为属性赋值
name = name_;
age = age_;
gender = gender_;
score = score_;
}
}
//定义教师类
class Teacher {
String name;
int age;
char gender;
double salary; //薪水
}
5、this关键字
用在构造器中,引用当前对象实例,用于访问当前对象的属性和方法,以后的构造方法中的参数的名字和类中的属性名都是一致的。
/**
* @document: 定义一个类,一个java程序可以定义多个类,但是只能有一个public类
* 程序的入口main方法在public声明的类中。
* @Author:SmallG
* @CreateTime:2023/7/25+10:21
*/
public class Demo02 {
public static void main(String[] args) {
Student student1 =new Student("Tom",21,'男',85.5);
Student student2 =new Student("Jerry",19,'男',86.5);
System.out.println("学生1的信息");
System.out.println("姓名:" + student1.name);
System.out.println("性别:" + student1.gender);
System.out.println("年龄:" + student1.age);
System.out.println("成绩:" + student1.score);
System.out.println("学生2的信息");
System.out.println("姓名:" + student2.name);
System.out.println("性别:" + student2.gender);
System.out.println("年龄:" + student2.age);
System.out.println("成绩:" + student2.score);
}
}
//定义(声明)一个类
class Student {
//属性(变量)
String name; //名字
int age; //年龄
char gender; //性别
double score;
//定义一个构造方法
public Student(String name, int age, char gender, double score) {
//为属性赋值,this为创建的对象
this.name = name;
this.age = age;
this.gender = gender;
this.score = score;
}
}
//定义教师类
class Teacher {
String name;
int age;
char gender;
double salary; //薪水
}
6、默认构造器
如果一个类没有声明任何构造器,那么系统会生成一个默认构造器。
如果类中定义了构造器,则不再添加默认构造器,需要自己添加一个默认构造器。
/**
* @document: 定义一个类,一个java程序可以定义多个类,但是只能有一个public类
* 程序的入口main方法在public声明的类中。
* @Author:SmallG
* @CreateTime:2023/7/25+10:21
*/
public class Demo02 {
public static void main(String[] args) {
Student student1 = new Student("Tom", 21, '男', 85.5);
Student student2 = new Student("Jerry", 19, '男', 86.5);
System.out.println("学生1的信息");
System.out.println("姓名:" + student1.name);
System.out.println("性别:" + student1.gender);
System.out.println("年龄:" + student1.age);
System.out.println("成绩:" + student1.score);
System.out.println("学生2的信息");
System.out.println("姓名:" + student2.name);
System.out.println("性别:" + student2.gender);
System.out.println("年龄:" + student2.age);
System.out.println("成绩:" + student2.score);
//默认构造器
Teacher teacher1 = new Teacher();
}
}
//定义(声明)一个类
class Student {
//属性(变量)
String name; //名字
int age; //年龄
char gender; //性别
double score;
//默认构造器
public Student(){
}
//构造器
public Student(String name, int age, char gender, double score) {
this.name = name;
this.age = age;
this.gender = gender;
this.score = score;
}
}
//定义教师类
class Teacher {
String name;
int age;
char gender;
double salary; //薪水
}
7、构造器重载
指在一个类中同时声明多个参数不同的构造器。构造器重载的好处是可以有更多的对象创建方式,使用起来更加灵活方便,用this时慎重。
/**
* @document: 定义一个类,一个java程序可以定义多个类,但是只能有一个public类
* 程序的入口main方法在public声明的类中。
* @Author:SmallG
* @CreateTime:2023/7/25+10:21
*/
public class Demo02 {
public static void main(String[] args) {
//创建一个学生对象 只提供了姓名 设置学生的年龄默认为18
Student student4 = new Student("Spike");
System.out.println("学生4的信息");
System.out.println("姓名:" + student4.name);
System.out.println("年龄:" + student4.age);
}
}
//定义(声明)一个类
class Student {
//属性(变量)
String name; //名字
int age; //年龄
char gender; //性别
double score;
//默认构造器
public Student(){
}
//只有名字的构造器
public Student(String name) {
this(18); //调用只有年龄参数的构造器 this必须写在第一行
this.name = name;
}
//只有年龄的构造器
public Student(int age) {
this.age = age;
}
public Student(String name, int age, char gender, double score) {
this.name = name;
this.age = age;
this.gender = gender;
this.score = score;
}
}
三、方法的声明和调用
1、声明方法
定义方法五要素:修饰词、返回值类型、方法名、参数列表、方法体
public static int sum (int num1,int num2){
// 修饰词 返回值类型 方法名 参数列表
方法体
}
/**
* @document: 方法
* @Author:SmallG
* @CreateTime:2023/7/25+15:14
*/
public class Demo03 {
public static void main(String[] args) {
//调用方法首先要创建对象
Person person = new Person();
//调用方法
person.say();
person.talk("SmallG");
int sum = person.sum(1, 2);
System.out.println(sum);
}
}
class Person {
//定义一个无参方法
public void say() {
System.out.println("Hello World");
}
//定义一个有参的方法
public void talk(String name) {
System.out.println("My name is " + name);
}
//定义包含多个参数的方法
public int sum(int num1, int num2) {
return num1 + num2;
}
}
银行实例练习:
/**
* @document: 银行账户
* @Author:SmallG
* @CreateTime:2023/7/25+15:31
*/
public class Account {
double balance; //账户余额
String log; //账户日志
public Account() {
this.balance = 1000.0; //初始余额
}
//存钱的方法 第一参数是存钱金额 第二参是交易记录
public void add(double amount, String message) {
this.balance += amount;
this.log += "收入:" + amount + ",当前余额:" + balance + ",备注:" + message;
System.out.println(log);
}
//查看余额是否充足
public boolean isEnough(double amount) {
boolean isEnough = this.balance - amount >= 0;
return isEnough;
}
}
2、方法的重载
一个类中,不可以有两个方法的签名完全相同,如果一个类的两个方法知识方法名相同,而参数列表不同是可以的。不同指的是类型和个数至少有一个不同。
/**
* @document: 银行账户
* @Author:SmallG
* @CreateTime:2023/7/25+15:31
*/
public class Account {
double balance; //账户余额
String log; //账户日志
public Account() {
this.balance = 1000.0; //初始余额
}
//存钱的方法 第一参数是存钱金额 第二参是交易记录
public void add(double amount, String message) {
this.balance += amount;
this.log += "收入:" + amount + ",当前余额:" + balance + ",备注:" + message;
System.out.println(log);
}
//方法的重载 没有备注的存钱
public void add(double amount) {
this.balance += amount;
this.log += "收入:" + amount + ",当前余额:" + balance+"备注:无";
System.out.println(log);
}
//查看余额是否充足
public boolean isEnough(double amount) {
boolean isEnough = this.balance - amount >= 0;
return isEnough;
}
}
四、Java对象内存管理
1、内存管理规则
JVM内存分为3个区域:方法区、栈、堆
1、方法区是一个静态区,Java的类(.class)以及类中方法都加载到这个区域,在使用类之前Java会将类自动加载到方法区,只加载一次。
2、栈是Java局部变量的空间,局部变量是指在方法中声明的变量,包括方法参数和this都是局部变量。在方法运行期间所有局部变量都在栈中分配,当方法结束时候方法中分配的局部变量。
3、堆是Java对象空间,Java的全部对象都在堆中分配,按照对象的属性在堆内存中分配对象的存储空间。对象使用以后,当对象不再被引用时候,对象变成内存垃圾,Java垃圾回收器会自动回收内存垃圾。
2、*创建对象的过程
具体流程如下:
- 把Memory.class文件和Person.class文件加载进内存的方法区
- 在栈内存中,开辟空间,存放变量p
- 在堆内存中,开辟空间,存放Person对象
- 对成员变量进行默认的初始化
- 对成员变量进行显式初始化
- 执行构造方法
- 堆内存完成
- 把堆内存的地址值赋值给变量p ,p就是一个引用变量,引用了Person对象的地址值
3、堆内存
用于存储使用new关键字所创建的对象
4、成员变量的生命周期
从对象在堆中创建开始到对象从堆中被回收结束。
Person p = new Person();
p = null ;
//不再指向刚分配的对象空间,成员变量失效
5、垃圾回收机制(GC)
是JVM自带的一个线程(自动运行的程序),用于回收没有任何引用指向的对象。垃圾回收机制自动进行回收管理。
6、内存泄露和内存溢出
内存泄露是指,不再被使用的内存没有被及时的回收,严重的内存泄露会因过多的内存占用而导致程序的崩溃。在程序中应该尽量避免不必要的内存浪费。
GC线程判断对象是否可以被回收的依据是该对象是否有引用来指向,因此,当确定该对象不再使用时,应该及时的将其引用设置为null,这样,该对象即不再被引用,属于可回收的范围。
7、栈
栈在运行结束后自动就没了,不用垃圾回收机制进行回收。
8、*成员变量和局部变量
成员变量与局部变量的差别如下:
局部变量:
- 定义在方法中
- 没有默认值,必须自行设定初始值
- 方法被调用时,存在栈中,方法调用结束时局部变量从栈中清除
成员变量:
- 定义在类中,方法外
- 由系统设定默认初始值,可以不显式初始化
- 所在类被实例化后,存在堆中,对象被回收时,成员变量失效
五、练习
1 设计类并创建对象
定义一个手机(Phone)类,要求:
- 包含属性为:品牌brand、型号style、价格price、硬盘大小diskSize(单位默认为GB)
- 包含一个方法showInfo,以字符串形式返回当前手机的全部信息
- 包含一个无参构造器和一个带4个参数的构造器,用于简化对象的创建
再编写一个测试类,在类中创建一个手机对象,并在控制台输出该手机对象的全部信息,运行效果如下所示:
/**
* @document: 设计类并创建对象
* @Author:SmallG
* @CreateTime:2023/7/25+8:14
*/
public class Phone {
static String brand; //品牌
static String style; //型号
static double price; //价格
static int diskSize; //硬盘大小
public Phone() {
}
//构造器
public Phone(String brand, String style, double price, int diskSize) {
this.brand = brand;
this.style = style;
this.price = price;
this.diskSize = diskSize;
}
public static String showInfo() {
return "Phone{" +
"brand='" + brand + '\'' +
", style='" + style + '\'' +
", price=" + price +
", diskSize=" + diskSize +
'}';
}
}
/**
* @document: Phone类的测试类
* @Author:SmallG
* @CreateTime:2023/7/25+8:18
*/
public class PhoneTest {
public static void main(String[] args) {
Phone phone = new Phone("华为", "P30 Pro", 3999.0, 128);
System.out.println(showInfo());
}
}
2 学生成绩管理
模拟学生成绩管理系统:录入3名学生的成绩,并提供信息查询功能。
程序设计要求:包含3个类
1、Student 类:表示学生,用于记载学生信息
- 包含id(整型),name(字符串),score(整型)三个属性
- 学生的成绩范围为0-100分
- Student类包含一个方法(getInfo),该方法以字符串形式返回当前学生的信息
/**
* @document: 设计类并创建对象
* @Author:SmallG
* @CreateTime:2023/7/25+8:14
*/
public class Phone {
static String brand; //品牌
static String style; //型号
static double price; //价格
static int diskSize; //硬盘大小
public Phone() {
}
//构造器
public Phone(String brand, String style, double price, int diskSize) {
this.brand = brand;
this.style = style;
this.price = price;
this.diskSize = diskSize;
}
public static String showInfo() {
return "Phone{" +
"brand='" + brand + '\'' +
", style='" + style + '\'' +
", price=" + price +
", diskSize=" + diskSize +
'}';
}
}
2、RecordSystem 类:表示记录系统,用于提供综合服务(录入数据、查询信息等)
- 包含一个 Student 数组类型的属性(studentArray),用于保存所有学生的信息,数组长度为3
- 包含两个方法,分别用于录入学生成绩(addScore)和显示学生信息(showInfo)
- addScore 方法:参数列表中包含2个整型参数,分别是学生id和学生分数,该方法的具体功能是更新studentArray数组中目标id的学生的score属性值
- showInfo方法:参数列表中没有参数,该方法的具体功能是按学生id顺序输出全部学生信息
- 在RecordSystem类的构造器中初始化studentArray数组,并添加3个学生对象到数组中,学生的id和姓名分别是:1 Tom,2 Jerry,3 Spike
/**
* @document: 表示记录系统,用于提供综合服务(录入数据,查询信息等)
* @Author:SmallG
* @CreateTime:2023/7/25+8:32
*/
public class RecordSystem {
//包含一个student数组类型的属性,用于保存所有学生的信息,数组长度为3
Student[] studentArray = new Student[3];
//初始化学生信息
public RecordSystem() {
studentArray[0] = new Student(1, "Tom");
studentArray[1] = new Student(2, "Jerry");
studentArray[2] = new Student(3, "Spike");
}
//方法一:录入学生成绩
public void addScore(int id, int score) {
//控制id和分数的范围
if (id < 1 || id > 3) {
System.out.println("输入有误,id超出范围!请重新输入!");
} else if (score < 0 || score > 100) {
System.out.println("分数录入有误,score有误!请重新输入");
} else {
studentArray[id - 1].score = score;
}
}
//方法二:显示学生信息
public void showInfo() {
for (int i = 0; i < studentArray.length; i++) {
System.out.println(studentArray[i].getInfo());
}
}
}
3、Main 主类
包含 main 方法,用于启动程序并和用户进行交互
- main方法中,首先创建RecordSystem类的对象和Scanner对象,然后输出菜 单,与用户进行交互
运行效果如下所示:
/**
* @document: 学生成绩系统
* @Author:SmallG
* @CreateTime:2023/7/25+8:28
*/
public class Student_Grade_System {
public static void main(String[] args) {
RecordSystem recordSystem = new RecordSystem();
Scanner scanner = new Scanner(System.in);
boolean s = true;
while (s){
System.out.println("欢迎使用学生成绩录入系统");
System.out.println("1 显示学生信息");
System.out.println("2 录入学生成绩");
System.out.println("3 退出");
System.out.println("请输入(1-3):");
int a = scanner.nextInt();
switch (a) {
case 1: //显示学生信息
System.out.println("---------------------------");
recordSystem.showInfo();
System.out.println("---------------------------");
break;
case 2: //录入学生成绩
System.out.println("---------------------------");
System.out.println("请输入学生id和分数,用空格分隔");
int id = scanner.nextInt();
int score = scanner.nextInt();
recordSystem.addScore(id, score);
recordSystem.showInfo();
System.out.println("---------------------------");
break;
case 3: //退出
System.out.println("谢谢使用");
System.out.println("---------------------------");
s = false;
break;
default:
System.out.println("输入有误,请重新输入");
break;
}
}
}
}
3 完善“学生成绩管理”
完善学生成绩系统系统,增加查看成绩排行功能,按照学生成绩由高到低显示学生的成绩。
提示:可能会用到快速拷贝数组的方法:Arrays.copyOf() 以及数组排序的方法。
运行效果如下所示:
将2中的代码进行修改:
/**
* @document: Student类,表示学生,用于记载学生信息
* @Author:SmallG
* @CreateTime:2023/7/25+8:29
*/
public class Student {
int id;
String name;
int score;
public Student() {
}
public Student(int id, String name){
this.id = id;
this.name = name;
}
//获取学生信息
public String getInfo() {
return "Student\t" + id + "\t" + name + "\t" + score;
}
}
/**
* @document: 表示记录系统,用于提供综合服务(录入数据,查询信息等)
* @Author:SmallG
* @CreateTime:2023/7/25+8:32
*/
public class RecordSystem {
//包含一个student数组类型的属性,用于保存所有学生的信息,数组长度为3
Student[] studentArray = new Student[3];
//初始化学生信息
public RecordSystem() {
studentArray[0] = new Student(1, "Tom");
studentArray[1] = new Student(2, "Jerry");
studentArray[2] = new Student(3, "Spike");
}
//方法一:录入学生成绩
public void addScore(int id, int score) {
//控制id和分数的范围
if (id < 1 || id > 3) {
System.out.println("输入有误,id超出范围!请重新输入!");
} else if (score < 0 || score > 100) {
System.out.println("分数录入有误,score有误!请重新输入");
} else {
studentArray[id - 1].score = score;
}
}
//方法二:显示学生信息
public void showInfo() {
for (int i = 0; i < studentArray.length; i++) {
System.out.println(studentArray[i].getInfo());
}
}
//方法三:查询整体成绩
//提示:可能会用到快速拷贝数组的方法:Arrays.copyOf() 以及数组排序的方法。
public void checkScore() {
//拷贝数组
Student[] students = Arrays.copyOf(studentArray, studentArray.length);
for (int i = 0; i < students.length - 1; i++) {
for (int j = 0; j < students.length - 1 - i; j++) {
if (students[j].score > students[j + 1].score) {
Student t = students[j];
students[j] = students[j + 1];
students[j + 1] = t;
}
}
}
for (int i = students.length - 1; i >= 0; i--) {
System.out.println(students[i].getInfo());
}
}
}
/**
* @document: 学生成绩系统
* @Author:SmallG
* @CreateTime:2023/7/25+8:28
*/
public class Student_Grade_System {
public static void main(String[] args) {
RecordSystem recordSystem = new RecordSystem();
Scanner scanner = new Scanner(System.in);
boolean s = true;
while (s){
System.out.println("欢迎使用学生成绩录入系统");
System.out.println("1 显示学生信息");
System.out.println("2 录入学生成绩");
System.out.println("3 查询整体成绩");
System.out.println("4 退出");
System.out.println("请输入(1-3):");
int a = scanner.nextInt();
switch (a) {
case 1: //显示学生信息
System.out.println("---------------------------");
recordSystem.showInfo();
System.out.println("---------------------------");
break;
case 2: //录入学生成绩
System.out.println("---------------------------");
System.out.println("请输入学生id和分数,用空格分隔");
int id = scanner.nextInt();
int score = scanner.nextInt();
recordSystem.addScore(id, score);
recordSystem.showInfo();
System.out.println("---------------------------");
break;
case 3: //查询整体成绩
System.out.println("---------------------------");
recordSystem.checkScore();
System.out.println("---------------------------");
break;
case 4: //退出
System.out.println("谢谢使用");
System.out.println("---------------------------");
s = false;
break;
default:
System.out.println("输入有误,请重新输入");
break;
}
}
}
}