面向对象(基础篇)
1. 概述
1.1 什么是面向对象
- 面向对象关注的是
类
:在计算机程序设计的过程中,参照现实的事物,将事物的属性特征、行为特征抽象出来,用类来表示。 - 代码结构:以
类
为组织单位,每种事物都具备自己的属性
和行为/功能
- 是一种
设计者思维
,适合解决复杂问题,代码扩展性强,可维护性高。
1.2 面向对象内容的三条主线
- JAVA类及;类的成员:(重点)
属性
、方法
、(熟悉)构造器、代码块,内部类。 - 面向对象的三大特征:
封装
,继承
,多态
,(抽象)。 - 其他关键字的使用:this、super、package、import、static、final、interface、abstract...等
1.3 面向对象完成具体功能的操作流程(重要)
- 创建类,设计类的内部成员(属性,方法)
- 创建类的对象例如
Phone phone = new Phone();
(类的实例化) - 通过对象,调用类中定义的方法和属性实现具体的功能
package com.ygc.oop.day01;
/**
* @author: YGC
* @createTime: 2023/08/22 0:18
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 手机测试
*/
public class PhoneTest {
public static void main(String[] args) {
//创建一个对象
Phone phone = new Phone();
//使用一个对象
//通过类中定义的属性和方法,来使用一个对象
phone.name = "YGC";
phone.price = 100;
phone.call();
phone.playGame();
phone.sendMessage("123");
System.out.println("名字是" + phone.name + "价格" + phone.price);
}
}
2. 基本元素(类,对象)
2.1 类
类
:具有相同特征的事物的抽象描述,是抽象的
、概念上的定义。
设计类,其实就是设计类的成员
-
类的实例化
-
等价描述:类的实例化<=>创建类的对象<=>创建类的实例
-
格式:类的类型 对象名 = 通过new创建对象的实体
-
//创建一个对象 Phone phone = new Phone();
-
2.2 类的内部成员
- 成员之一:
属性
(成员变量,filed) - 成员至二:(成员方法)
方法
,(函数、method)
2.3 类的成员之一:属性(成员变量,filed)
- 变量的分类:
- 角度一:按照数据类型,基本数据类型:8种。引用数据类型:(数组,类,枚举,注解,记录)
- 角度二:按照定义的位置分类,成员变量(属性),局部变量(方法内,构造器内,代码块内)
-
属性的几个称谓:
成员变量
,filed, -
区分
成员变量
vs局部变量
3.1 相同点:
- 都是变量,符合变量的定义规范
- 必须先定义后使用
- 有各自的
作用域
,出了作用域就失效
3.2 不同点:
① 类中声明的位置的不同:
- 成员变量声明在
类
里 - 局部变量声明在
方法
里
② 在内存中分配的位置不同:
- 成员变量存放在
堆
中 - 局部变量存储在
栈
中
③ 生命周期:
-
成员变量:随着
对象
的创建而创建 -
局部变量:随着方法的
栈帧
进入栈中被激活,随着方法的栈帧出栈消亡
④ 作用域:
-
成员变量:作用在
整个类
中 -
局部变量:只作用在定义局部变量的方法或构造器,代码块中
⑤ 是否可以有权限修饰符进行修饰:
- public,protect,缺省,private
- 只有
成员变量
可以被权限修饰符
修饰
⑥ 是否有默认值:
- 成员变量:具有初始值,与
数组的初始值
相同 - 局部变量:没有初始值,定义时必须赋给初始值,才能使用,否则会报错
- 注意:对于
方法
的形参而言,在调用方法
时,给此形参赋值即可。
public void sleep(int hour) {
System.out.println("人每天至少睡眠" + hour + "小时");
}
//在调用方法时给huor赋值,不赋值无法调用方法
p1.sleep(8);
2.3.1 任何一个数据类型都可以是成员变量的类型。
- 举例
package com.ygc.oop.day02.filed02;
/**
* @author: YGC
* @createTime: 2023/08/22 23:40
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 声明一个MyDate类型,有属性:年year,月month,日day
*/
public class MyDate {
int year;//年
int mouth;//月
int day;//日
}
package com.ygc.oop.day02.filed02;
/**
* @author: YGC
* @createTime: 2023/08/22 23:42
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 声明一个Employee类型,包含属性:编号、姓名、年龄、薪资、生日(MyDate类型)
*/
public class Employee {
int id;//编号
String name;//姓名
int age;//年龄
double salary;//薪资
MyDate myDate;//生日***对象类型
}
package com.ygc.oop.day02.filed02;
/**
* @author: YGC
* @createTime: 2023/08/22 23:43
* @blogs: https://www.cnblogs.com/ygcDiary
* @description:在EmployeeTest测试类中的main()中,创建两个员工对象,并为他们的姓名和生日赋值,并显示
*/
public class EmployeeTest {
public static void main(String[] args) {
Employee employee = new Employee();
MyDate myDate = new MyDate();
Employee employee1 = new Employee();
employee.name = "jack";
/*另一种写法
* MyDate myDate1 = new MyDate();
employee.myDate = myDate;
* */
employee.myDate = new MyDate();
employee.myDate.mouth = 2;
employee.myDate.day = 3;
System.out.println(employee.name + "的生日是" + employee.myDate.mouth + "月" + employee.myDate.day + "日");
}
}
2.4 类的成员之二:方法(method)
- 方法是类或对象行为的抽象,用来完成具体的功能
- 将功能封装在方法里面,可以
实现代码的重用,减少代码的冗余,简化代码
- JAVA中方法不能
单独存在
,必须声明在类中 - 方法在使用完成之后就出栈。
-
方法的声明
权限修饰符 [其他修饰符] 返回值类型 方法名(形参列表){ //方法体 } //[其他修饰符]不是必须要写的
1.1 权限修饰符
-
public ,private,protect,缺省
1.2 返回值类型:描述调用完此方法,是否需要返回一个结果
-
无返回值:void
-
有返回值:可以是基本数据类型,也可以是引用数据类型。
- 在 有返回值 的类型中需要配合
return+返回值类型的数据或常量
- 【经验:】根据方法实现的功能,来判断是否需要返回值
- 在 有返回值 的类型中需要配合
1.3 形参列表:是局部变量
- 格式:(形参类型1 形参1,形参类型2 形参2,......)
- 有形参:根据方法调用时
不确定因素的个数
,确定形参的个数 - 无形参:不可以省略()
- return返回值
- 作用1:结束一个方法
- 作用2:结束方法的同时返回数据给方法的调用者
- return后
不能有执行语句
2.4.1 注意事项
- JAVA中的方法,不调用不执行,调用一次,执行一次,在main开始之前,先在
方法区
加载类 - 方法的定义不分先后顺序。在方法中可以调用其他的方法,不分前后。
- 方法中可以调用方法或属性
- 方法内不能定义方法,方法与方法是平级关系
举例:
package com.ygc.oop.day02.method;
/**
* @author: YGC
* @createTime: 2023/08/23 0:54
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 方法
*/
public class PersonMethod {
public static void main(String[] args) {
Person person = new Person();
person.eat();
}
class Person {
public void eat() {
System.out.println("人吃饭");
sleep(8);
}
public String sleep(int hour) {
String info = "人每天至少睡眠" + hour + "小时";
System.out.println(info);
return info;
}
}
}
2.4.2 方法的使用
System.out.println(employee1.show());无返回值报错,有返回值不报错
package com.ygc.oop.day02.method;
/**
* @author: YGC
* @createTime: 2023/08/22 20:17
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 员工测试
* 案例:
* 声明员工类Employee,包含属性:编号id、姓名name、年龄age、薪资salary。
* 声明EmployeeTest测试类,并在main方法中,创建2个员工对象,并为属性赋值,并打印两个员工的信息。
*/
public class EmployeeTest {
public static void main(String[] args) {
Employee employee1 = new Employee();
employee1.id = 1;
employee1.name = "jack";
employee1.age = 24;
employee1.salary = 6900;
Employee employee2 = new Employee();
employee2.id = 2;
employee2.name = "rose";
employee2.age = 25;
employee2.salary = 8900;
/* System.out.println("员工id" + employee1.id + "姓名" + employee1.name + "年龄:" + employee1.age + "薪资:" + employee1.salary);*/
//替换为
employee1.show();
// System.out.println(employee1.show());无返回值报错,有返回值不报错
// System.out.println("员工id" + employee2.id + "姓名" + employee2.name + "年龄:" + employee2.age + "薪资:" + employee2.salary);
//替换为
employee2.show();
}
}
package com.ygc.oop.day02.method;
/**
* @author: YGC
* @createTime: 2023/08/22 20:17
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 员工
* 声明员工类Employee,包含属性:编号id、姓名name、年龄age、薪资salary。
*/
public class Employee {
int id;//编号
String name;//姓名
int age;//年龄
double salary;//薪资
public void show() {
System.out.println("员工id" + id + "姓名" + name + "年龄:" + age + "薪资:" + salary);
}
}
2.4.3 方法的内存结构
2.4.4 方法的使用(练习)
2.4.4.1数组工具类
package com.ygc.oop.day02.methodExer.Exer04;
/**
* @author: YGC
* @createTime: 2023/08/23 12:44
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 数组工具类
* 案例:
* 根据上一章数组中的常用算法操作,自定义一个操作int[]的工具类。
* 涉及到的方法有:求最大值、最小值、总和、平均数、遍历数组、复制数组、数组反转、数组排序(默认从小到大排序)、查找等
*/
public class MyArray {
//属性
int sum;
int avg;
int max;
int min;
/**
* 求数组最大值
*
* @param arr
*/
public void max(int[] arr) {
max = arr[0];
for (int i = 0; i < arr.length; i++) {
if (max < arr[i]) {
max = arr[i];
}
}
System.out.println("最大值是:" + max);
}
/**
* 最小值
*
* @param arr
*/
public void min(int[] arr) {
min = arr[0];
for (int i = 0; i < arr.length; i++) {
if (min > arr[i]) {
min = arr[i];
}
}
System.out.println("最小值是:" + min);
}
/**
* 总和
*
* @param arr
*/
public int sum(int[] arr) {
sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
/**
* 平均数
*
* @param arr
*/
public void avg(int[] arr) {
avg = sum(arr) / arr.length;
System.out.println("平均值是:" + avg);
}
/**
* 遍历数组
*
* @param arr
*/
public void forArrays(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "\t");
}
System.out.println();
}
/**
* 复制数组
*
* @param arr
*/
public void copyArrays(int[] arr) {
int[] arr1 = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
arr1[i] = arr[i];
}
forArrays(arr);
System.out.println();
}
/**
* 数组反转
*
* @param arr
*/
public void rollBackArrays(int[] arr) {
int temp = 0;
for (int i = 0; i < arr.length / 2; i++) {
temp = arr[i];
arr[i] = arr[arr.length - 1 - i];
arr[arr.length - 1 - i] = temp;
}
forArrays(arr);
System.out.println();
}
/**
* 数组排序
*
* @param arr
*/
public void sortArrays(int[] arr) {
int temp = 0;
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length; j++) {
if (arr[i] < arr[j]) {
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
forArrays(arr);
System.out.println();
}
/**
* 数组查找
*
* @param arr 数组
* @param target 目标值
*/
public void search(int[] arr, int target) {
sortArrays(arr);
int head = 0;
int end = arr.length - 1;
boolean isFlag = false;
while (head <= end) {
int mid = (head + end) / 2;
if (arr[mid] > target) {
end = mid - 1;
} else if (arr[mid] < target) {
head = mid + 1;
} else {
System.out.println("找到了" + mid);
isFlag = true;
break;
}
}
if (!isFlag) {
System.out.println("未找到");
}
}
}
- 数组工具类测试
package com.ygc.oop.day02.methodExer.Exer04;
/**
* @author: YGC
* @createTime: 2023/08/23 13:00
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 数组工具类测试
* 求最大值、最小值、总和、平均数、遍历数组、复制数组、数组反转、数组排序(默认从小到大排序)、查找等
*/
public class MyArrayTest {
public static void main(String[] args) {
MyArray myArray = new MyArray();
int[] arr = {77, 88, 5, 6, 1, 3};
//总和
System.out.println("======总和=======");
System.out.println(myArray.sum(arr));
//最大值
System.out.println("=======最大值======");
myArray.max(arr);
//最小值
System.out.println("======最小值=======");
myArray.min(arr);
//平均值
System.out.println("======平均值=======");
myArray.avg(arr);
//数组遍历
System.out.println("======数组遍历=======");
myArray.forArrays(arr);
//数组复制
System.out.println("=====数组复制========");
myArray.copyArrays(arr);
//数组反转
System.out.println("=====数组反转========");
myArray.rollBackArrays(arr);
//数组排序
System.out.println("=====数组排序========");
myArray.sortArrays(arr);
//数组查找目标值
System.out.println("=====数组查找目标值========");
myArray.search(arr, 3);
}
}
2.4.5 方法的重载
在类中不允许定义相同的方法
-
定义:在同一个类中,允许存在多个重名,但参数列表不同的方法
-
两同一不同:
- 两同:同一个
类
中。方法名
相同 - 不同:
参数列表
不同
- 两同:同一个
-
方法的重载与
权限修饰符,形参名,返回值类型
都没有关系 -
举例
-
package com.ygc.oop.day02.method_overload; /** * @author: YGC * @createTime: 2023/08/23 23:17 * @blogs: https://www.cnblogs.com/ygcDiary * @description: 方法的重载 */ public class OverTest { public void add(int a) { } public void add(int a, int b) { } public void add(int a, String b) { } public void add(String b, int a) { } /*public void add(int m, int n){ }*/ /*public int add(String b, int a) { return 0; }*/ }
经典面试题
-
package com.ygc.oop.day02.method_overload; /** * @author: YGC * @createTime: 2023/08/23 23:39 * @blogs: https://www.cnblogs.com/ygcDiary * @description: 方法的重载面试题 */ public class InterviewTest { public static void main(String[] args) { int[] arr = new int[]{1, 2, 3}; System.out.println(arr);//地址值 char[] arr1 = new char[]{'a', 'b', 'c'}; System.out.println(arr1);//abc String[] arr2 = new String[]{"ass", "aaa", "ab"}; System.out.println(arr2);//地址值 } } //底层println方法发生了重载,char类型的打印会遍历数组
-
2.4.6 可变形参个数的方法
- 在定义方法时,形参类型确定,但是参数的个数不确定
- 格式:
(参数类型...参数名)
- 可以构成
重载
(除了数组类型之外) - 可以类比成
数组
- 可变个数的形参必须放在参数列表的
最后
(int a,int...num) - 可变个数的形参在同一个方法的参数列表,最多只能
出现一次
举例:
package com.ygc.oop.day02.method_args;
/**
* @author: YGC
* @createTime: 2023/08/23 23:53
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 可变参数个数的方法
*/
public class ArgsTest {
public static void main(String[] args) {
ArgsTest argsTest = new ArgsTest();
argsTest.print();//这是一个参数个数可变的方法111
argsTest.print(11);//这是一个参数个数可变的方法222
argsTest.print(111, 222);//这是一个参数个数可变的方法333
}
public void print(int... num) {
System.out.println("这是一个参数个数可变的方法111");
}
public void print(int i) {
System.out.println("这是一个参数个数可变的方法222");
}
public void print(int i, int j) {
System.out.println("这是一个参数个数可变的方法333");
}
}
- 练习
package com.ygc.oop.day02.method_args;
/**
* @author: YGC
* @createTime: 2023/08/24 0:13
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 可变参数个数的方法的练习
* 练习:可变形参的方法
* n个字符串进行拼接,每一个字符串之间使用某字符进行分割,如果没有传入字符串,那么返回空字符串""
*/
public class ArgsTest01 {
public static void main(String[] args) {
ArgsTest01 argsTest01 = new ArgsTest01();
String info = argsTest01.print("-", "hello", "world");
System.out.println(info);
}
public String print(String operator, String... strs) {
String result = " ";
for (int i = 0; i < strs.length; i++) {
if (i == 0) {
result += strs[i];
} else {
result += operator + strs[i];
}
}
return result;
}
}
2.4.7 递归方法
- 定义一个方法,方法自己调用自己
- 分类: 直接递归,间接递归
- 说明:
-
递归方法是一种隐式的循环
-
如果这种循环不能停止,则会报错(栈内存溢出)
递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环
慢的多
,所以在使用递归时要慎重。在要求高性能的情况下尽量避免使用递归,递归调用既花时间又
耗内存
。考虑使用循环迭代
2.4.7.1 练习
package com.ygc.oop.day02.recursion;
/**
* @author: YGC
* @createTime: 2023/08/24 14:57
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 递归方法
*/
public class RecursionExer01 {
public static void main(String[] args) {
RecursionExer01 recursionExer01 = new RecursionExer01();
System.out.println(recursionExer01.method01(100));
System.out.println(recursionExer01.method02(3));
System.out.println(recursionExer01.method03(10));
System.out.println(recursionExer01.method04(10));
System.out.println(recursionExer01.method05(10));
}
//1-100的和
public int method01(int num) {
if (num == 1) {
return 1;
} else {
return method01(num - 1) + num;
}
}
//n!阶乘
public int method02(int num) {
if (num == 1) {
return 1;
} else {
return num * method02(num - 1);
}
}
/*举例3:已知有一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),其中n是大于0的整数,求f(10)的值。*/
public int method03(int num) {
if (num == 0) {
return 1;
} else if (num == 1) {
return 4;
} else {
return 2 * method03(num - 1) + method03(num - 2);
}
}
/*举例4:已知一个数列:f(20) = 1,f(21) = 4,f(n+2) = 2*f(n+1)+f(n),其中n是大于0的整数,求f(10)的值。*/
public int method04(int num) {
if (num == 20) {
return 1;
} else if (num == 21) {
return 4;
} else {
return method04(num + 2) - 2 * method04(num + 1);
}
}
/*举例5:计算斐波那契数列(Fibonacci)的第n个值,斐波那契数列满足如下规律*/
public int method05(int num) {
if (num == 1 || num == 2) {
return 1;
} else {
return method05(num - 1) + method05(num - 2);
}
}
}
2.6 值传递机制
2.6.1 基本数据类型和引用数据类型的值传递机制
package com.ygc.oop.day02.vlaueTransfer;
/**
* @author: YGC
* @createTime: 2023/08/24 12:36
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 值传递机制
*/
public class ValueTransferTest {
public static void main(String[] args) {
//1.基本数据类型
int m = 10;
int n = m;//传递的是数据值
System.out.println("m=" + m + ",n=" + n);//m=10,n=10
m++;
System.out.println("m=" + m + ",n=" + n);//m=11,n=10
//2.引用数据类型
int[] arr = {1, 2, 3, 4, 5, 6};
int[] arr2 = arr;//传递的是地址值
arr2[0] = 10;
System.out.println(arr[0]);//10
//3.对象类型
Person person = new Person();
person.id = 1001;
Person person1 = person;//传递的是地址值
person1.id = 1002;
System.out.println(person.id);//1002
}
}
class Person {
int id;
}
2.6.2 方法的值传递机制
- 实例引入
package com.ygc.oop.day02.vlaueTransfer;
package com.ygc.oop.day02.vlaueTransfer;
/**
* @author: YGC
* @createTime: 2023/08/24 12:57
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 方法的值传递机制
*/
public class ValueTransferTest01 {
public static void main(String[] args) {
ValueTransferTest01 valueTransferTest01 = new ValueTransferTest01();
//基本数据类型
int m = 10;
valueTransferTest01.method01(m);
System.out.println(m);//10
//引用数据类型
Person01 p = new Person01();
p.age = 10;
valueTransferTest01.method02(p);
System.out.println(p.age);//11
}
public void method01(int m) {
m++;
}
public void method02(Person01 p) {
p.age++;
}
}
class Person01 {
int age;
}
2.6.2.1 总结(不管是基本数据类型,引用数据类型,还是方法,都是值传递的机制)
- 对于方法内声明的变量,出现赋值操作,分为两种情况
- 如果是基本数据类型的变量:直接赋
数据
- 如果是引用数据类型的变量:赋值操作,赋值的是
地址值
2.6.4 练习
package com.ygc.oop.day02.vlaueTransfer;
/**
* @author: YGC
* @createTime: 2023/08/24 13:19
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 方法的值传递机制练习
*/
public class ValueTransferTest03 {
public static void main(String[] args) {
Method method = new Method();
method.m = 10;
method.n = 20;
method.swap(method);
System.out.println("m= " + method.m + " n= " + method.n);//m =20,n =10
}
}
class Method {
int m;
int n;
public void swap(Method method) {
int temp = 0;
temp = method.m;
method.m = method.n;
method.n = temp;
}
}
package com.ygc.oop.day02.vlaueTransfer;
/**
* @author: YGC
* @createTime: 2023/08/24 13:12
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 方法的值传递机制练习
*/
public class ValueTransferTest02 {
public static void main(String[] args) {
ValueTransferTest02 valueTransferTest02 = new ValueTransferTest02();
int m = 10;
int n = 20;
//交换前
System.out.println("m" + m + ",n" + n);
valueTransferTest02.swap(m, n);//10,20
//交换后
System.out.println("m" + m + ",n" + n);//10,20
}
public void swap(int m, int n) {
int temp = 0;
temp = m;
m = n;
n = temp;
}
}
package com.ygc.oop.day02.valueTransferTest;
/**
* @author: YGC
* @createTime: 2023/08/24 13:45
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 2. 定义一个类PassObject,在类中定义一个方法printAreas(),该方法的定义如下:
* public void printAreas(Circle c, int time)。
* 3. 在printAreas方法中打印输出1到time之间的每个整数半径值,以及对应的面积。
* 例如,time为5,则输出半径1,2,3,4,5,以及对应的圆面积。
* 4. 在main方法中调用printAreas()方法,调用完毕后输出当前半径值。程序运行结果如图所示
*/
public class PassObject {
public static void main(String[] args) {
PassObject passObject = new PassObject();
Circle c = new Circle();
passObject.printAreas(c, 5);
System.out.println("now radius is:" + c.radius);
}
public void printAreas(Circle c, int time) {
System.out.println("radius\t\tarea");
for (int i = 1; i <= time; i++) {
c.radius = i;
System.out.println(c.radius + "\t\t\t" + c.findArea());
}
c.radius = time + 1;
}
}
package com.ygc.oop.day02.valueTransferTest;
/**
* @author: YGC
* @createTime: 2023/08/24 13:42
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 圆
* 1. 定义一个Circle类,包含一个double型的radius属性代表圆的半径,一个findArea()方法返回圆的面积。
*/
public class Circle {
double radius;
public double findArea() {
return Math.PI * radius * radius;
}
}
package com.ygc.oop.day02.valueTransferTest.exer02;
/**
* @author: YGC
* @createTime: 2023/08/24 14:11
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 真题
*/
public class InterviewTest {
public static void main(String[] args) {
int a = 10;
int b = 10;
InterviewTest interviewTest = new InterviewTest();
interviewTest.method(a, b);
System.out.println(a);
System.out.println(b);
}
public void method(int a, int b) {
a = 100;
b = 200;
System.out.println(a);
System.out.println(b);
//退出,不执行上面的代码
System.exit(0);
}
}
2. 7对象
对象
:实际存在的该类事物,每个个体
,是具体的
,因此也称为实例(insrance)
。
2.7.1 对象在内存中分配涉及到的内存结构
- 栈:方法内定义的变量,存储在栈中。
- 堆:new出来的结构(比如:数组的实体,对象的实体)。包括对象的属性
- 方法区:存放类的字节码文件,(类的模板)
2..2 对象的内存解析
2..2.1 创建类的一个对象
2..2.2 多个类的多个对象
2..3 注意事项
Person p1 = new Person();
Person p2 = new Person();
//创建两个对象
- 创建类的多个对象时,每个对象在堆空间中有一个对象的实体,每个对象的实体保存着一份属性
- 如果修改对象的某个属性时不会影响其他对象的属性
【相互独立】
Person p1 = new Person();
Person p3 = p1;//赋值操作
//创建一个对象
- 此时相当于将p1的地址值赋值给p3,在堆空间没有开辟新的空间存储新的对象属性,而是p1,p3公用一个对象的属性
- 此时通过p3修改对象的属性,p1相对应的属性也会被修改。
2..4 对象数组 (与二维数组类似)
- 什么是对象数组:
- 数组中的数据可以是基本数据类型,也可以是引用数据类型,因此,数组中的元素全部为对象,称为对象数组
- 举例:
- String[],Person[].....
- 案例
package com.ygc.oop.day02.methodExer.Exer05;
/**
* @author: YGC
* @createTime: 2023/08/23 14:06
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 学生对象测试
* 创建20个学生对象,学号为1到20,年级和成绩都由随机数确定。
*/
public class StudentTest {
public static void main(String[] args) {
Student[] students = new Student[20];
Student student = new Student();
student.inStudnet(students);
System.out.println("============所有学生信息===========");
student.showStudent(students);
System.out.println("===========三年级的成绩============");
student.showStudentWithState(students);
System.out.println("===========排序后的成绩============");
student.sortScore(students);
}
}
package com.ygc.oop.day02.methodExer.Exer05;
/**
* @author: YGC
* @createTime: 2023/08/23 14:05
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 学生类
* 案例:
* 1)定义类Student,包含三个属性:学号number(int),年级state(int),成绩score(int)。
* 2)创建20个学生对象,学号为1到20,年级和成绩都由随机数确定。
* 问题一:打印出3年级(state值为3)的学生信息。
* 问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息
*/
public class Student {
int number;//成绩
int state;//年级
int score;//学号
/**
* 冒泡排序,排序学生的成绩
*
* @param students
*/
public void sortScore(Student[] students) {
Student temp = null;
for (int i = 0; i < students.length; i++) {
for (int j = 0; j < students.length; j++) {
if (students[i].score < students[j].score) {
//交换学生的所有信息
temp = students[i];
students[i] = students[j];
students[j] = temp;
}
}
}
showStudent(students);
}
/**
* 打印三年级的学生信息
*
* @param students
*/
public void showStudentWithState(Student[] students) {
for (int i = 0; i < students.length; i++) {
if (students[i].state == 3) {
System.out.print("学号:" + students[i].number + "\t");
System.out.print("分数" + students[i].score + "\t");
System.out.print("年级" + students[i].state + "\t");
System.out.println();
}
}
}
/**
* 打印学生信息
*
* @param students
*/
public void showStudent(Student[] students) {
for (int i = 0; i < students.length; i++) {
System.out.print("学号:" + students[i].number + "\t");
System.out.print("分数" + students[i].score + "\t");
System.out.print("年级" + students[i].state + "\t");
System.out.println();
}
}
public void inStudnet(Student[] students) {
for (int i = 0; i < students.length; i++) {
students[i] = new Student();
students[i].number = i + 1;
students[i].state = (int) (Math.random() * 3) + 1;
students[i].score = (int) (Math.random() * 100) + 1;
}
}
}
- 内存解析
关键字
import:同名类导入,需要使用全类名
的方式区分。
Date date = new Date();
java.sql.Date sqlDate = new java.sql.Date(123456);
3. 面向过程
3.1 什么是面向过程
- 以
函数
组织单位 - 是一种
执行者思维
,适用于解决简单的问题。扩展能力差,不便于后期的维护。
4. 封装性
4.1 为什么需要封装性
- 高内聚,低耦合的体现:
- 高内聚:类的内部数据和操作细节由自己完成,不允许外界干涉
- 低耦合:仅仅暴露少量方法给外部使用。尽量方便外界调用。
高内聚、低耦合是软件工程中的概念,也是UNIX 操作系统设计的经典原则。
内聚,指一个模块内各个元素彼此结合的紧密程度;耦合指一个软件结构内不同模块之间互连程度的度量。内聚意味着重用和独立,耦合意味着多米诺效应牵一发动全身。
通俗的说:把该隐藏的隐藏,该暴露的暴露
4.2 如何实现数据封装
- 通过权限修饰符:
- Java 提供了四个权限修饰符public,protected,缺省,private
- 使用四种权限修饰符修饰类的内部成员,当这些成员被调用时
体现可见性的大小
- 实际案例
package com.ygc.day01_constructor;
/**
* @author: YGC
* @createTime: 2023/08/25 0:08
* @blogs: https://www.cnblogs.com/ygcDiary
* @description: 封装性的测试
*/
public class AnimalTest {
public static void main(String[] args) {
Animal animal = new Animal();
animal.name = "人";
animal.setLegs(2);
System.out.println(animal.getLegs());
animal.eat();
}
}
class Animal {
private int legs;
String name;
public void setLegs(int l) {
if (l > 0 && l % 2 == 0) {
legs = l;
} else {
System.out.println("你输入的数据有误");
}
}
public int getLegs() {
return legs;
}
public void eat() {
System.out.println("觅食");
}
}
说明:
- 在正常的条件下legs是不能小于0的,为了避免外部类赋值错误,我们可以将legs属性私有化,使外部类无法调用该属性,在内部类中可以提供一个
暴露的方法
,给外部类实现赋值操作 - 同时内部类也提供了一个
暴露的方法
,可以让外部类得到该属性的值 - 这就是数据的封装。
4.3 权限修饰符的范围
- public > protected > 缺省 > private
修饰符 | 本类内部 | 本包内 | 其他包的子类 | 其他包非子类 |
---|---|---|---|---|
private | √ | × | × | × |
缺省 | √ | × | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
4.4 四种权限的使用
4.1 权限修饰符的使用规则:
- 类:只能用public 和缺省修饰
- 类的内部成员:可以用4种权限修饰符修饰
4.2 使用频率
- 开发中4种权限修饰使用频率
- 比较高:public,private
- 不常用:缺省,protected
4.3 封装性的体现
-
私有化(private)属性,通提供公共的(public)get(),set()方法,对这个属性进行修改和获取
-
将类种不需要对外暴露的方法私有化
-
单例模式构造器私有化,避免在类的外部创建实例
4.4.1 练习
4.4.1.1 练习1,封装员工属性,并在键盘输入信息
package com.ygc.day01_constructor;
/**
* @author: YGC
* @createTime: 2023/08/25 12:45
* @blogs: <a>https://www.cnblogs.com/ygcDiary</a>
* @description: 员工
* 案例:普通员工类
* (1)声明员工类Employee,
* - 包含属性:姓名、性别、年龄、电话,属性私有化
* - 提供get/set方法
* - 提供String getInfo()方法
*/
public class Employee {
private String name;
private char gender;
private int age;
private String telPhone;
public void setName(String name1) {
name = name1;
}
public String getName() {
return name;
}
public void setGender(char gender1) {
gender = gender1;
}
public char getGender() {
return gender;
}
public void setAge(int age1) {
if (age1 > 0 && age1 < 130) {
age = age1;
} else {
System.out.println("你输入的年龄有误");
}
}
public int getAge() {
return age;
}
public void setTelPhone(String telPhone1) {
telPhone = telPhone1;
}
public String getTelPhone() {
return telPhone;
}
public String info() {
return name + "\t" + gender + "\t" + age + "\t" + telPhone + "\t";
}
}
package com.ygc.day01_constructor;
import java.util.Scanner;
/**
* @author: YGC
* @createTime: 2023/08/25 12:54
* @blogs: <a>https://www.cnblogs.com/ygcDiary</a>
* @description: 员工测试
* 2)在测试类的main中创建员工数组,并从键盘输入员工对象信息,最后遍历输出
*/
public class EmployeeTest {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入员工的个数:");
int employeeNumber = scanner.nextInt();
Employee[] employees = new Employee[employeeNumber];
for (int i = 0; i < employees.length; i++) {
//这一步没有就是空指针异常,因为没有对象数组里的初始值是null
employees[i] = new Employee();
System.out.println("添加第" + (i + 1) + "个员工");
System.out.print("姓名:");
employees[i].setName(scanner.next());
System.out.print("性别:");
employees[i].setGender(scanner.next().charAt(0));
System.out.print("年龄:");
employees[i].setAge(scanner.nextInt());
System.out.print("电话:");
employees[i].setTelPhone(scanner.next());
}
System.out.println("员工列表");
System.out.println("编号\t姓名\t性别\t年龄\t电话");
for (int i = 0; i < employees.length; i++) {
System.out.println((i + 1) + "\t" + employees[i].info());
}
}
}
5. 构造器(constructor)
凡是一个类,他都有自己的构造器
5.1 构造器概述
new对象,并在new对象的时候为实例变量赋值。
举例:Person p = new Person(“Peter”,15)
;
- 构造器的作用:
- 搭配关键字new 创建类的对象
- 在创建对象的同时,可以给相关的属性赋值
- 构造器的使用说明:
- 构造器的格式
[修饰符] class 类名{
[修饰符] 构造器名(){
// 实例初始化代码
}
[修饰符] 构造器名(参数列表){
// 实例初始化代码
}
}
-
构造器名必须与它所在的
类名
必须相同
。 -
它
没有返回值
,所以不需要返回值类型,也不需要void
。 -
构造器的修饰符只能是
权限修饰符
public,private,缺省,protected,不能是其他的 -
创建类之后,在没有显示提供任何构造器的情况下,系统在编译时会自动提供一个
空参的构造器
-
默认的空参构造器的权限与该类的权限相同
-
显示的创建了构造器,系统将不再提供默认的构造器
-
构造器可以发生重载
-
快捷键:在构建类的对象时,显示构造器的参数
ctrl+p
5.2 举例
package com.ygc.day01_constructor.exer_constructor;
/**
* @author: YGC
* @createTime: 2023/08/25 14:05
* @blogs: <a>https://www.cnblogs.com/ygcDiary</a>
* @description: 学生
* <p>
* 案例:
* (1)定义Student类,有4个属性:
* String name;
* int age;
* String school;
* String major;
* (2)定义Student类的3个构造器:
* - 第一个构造器Student(String n, int a)设置类的name和age属性;
* - 第二个构造器Student(String n, int a, String s)设置类的name, age 和school属性;
* - 第三个构造器Student(String n, int a, String s, String m)设置类的name, age ,school和major属性;
*/
public class Student {
String name;
int age;
String school;
String major;
public Student(String n, int a) {
name = n;
age = a;
}
public Student(String n, int a, String s) {
name = n;
age = a;
school = s;
}
public Student(String n, int a, String s, String m) {
name = n;
age = a;
school = s;
major = m;
}
}
package com.ygc.day01_constructor.exer_constructor;
/**
* @author: YGC
* @createTime: 2023/08/25 14:09
* @blogs: <a>https://www.cnblogs.com/ygcDiary</a>
* @description: 学生测试_构造器
* (3)在main方法中分别调用不同的构造器创建的对象,并输出其属性值。
*/
public class StudentTest {
public static void main(String[] args) {
Student student = new Student("ygc", 12);
Student student1 = new Student("ygc", 12, "南京");
Student student2 = new Student("ygc", 12, "南京", "计算机");
System.out.println(student.name + "\t" + student.age);
System.out.println(student1.name + "\t" + student1.age + "\t" + student1.school);
System.out.println(student2.name + "\t" + student2.age + "\t" + student2.school + "\t" + student2.major);
}
}
5.3 综合练习
- 银行类
package com.ygc.day01_constructor.exerAll_account;
/**
* @author: YGC
* @createTime: 2023/08/25 14:27
* @blogs: <a>https://www.cnblogs.com/ygcDiary</a>
* @description: 账户
* 案例:
* 1、写一个名为Account的类模拟账户。该类的属性和方法如下图所示。
* 该类包括的属性:账号id,余额balance,年利率annualInterestRate;
* 包含的构造器:自定义
* 包含的方法:访问器方法(getter和setter方法),取款方法withdraw(),存款方法deposit()。
*/
public class Account {
private int id;
private double balance;
private double annualInterestRate;
public Account() {
}
public Account(int i, double b, double a) {
id = i;
balance = b;
annualInterestRate = a;
}
public void setId(int i) {
id = i;
}
public int getId() {
return id;
}
public void setBalance(int b) {
balance = b;
}
public double getBalance() {
return balance;
}
public void setAnnualInterestRate(double annualInterestRate1) {
annualInterestRate = annualInterestRate1;
}
public double getAnnualInterestRate() {
return annualInterestRate;
}
//取款方法
public void withdraw(int outMoney) {
if (outMoney > balance) {
System.out.println("取款失败:余额不足");
} else {
System.out.println("取款成功:" + outMoney);
System.out.println("余额:" + (balance -= outMoney));
}
}
//存款方法deposit()
public void deposit(int inMoney) {
balance += inMoney;
System.out.println("存款成功,本次存款金额为:" + inMoney);
}
//输出信息
public String customerInfo(Customer customer) {
return "Customer [" + customer.getFirstName() + "," + customer.getLastName() + "] has a account: id is " + getId() + "," + getAnnualInterestRate() + "is" + getAnnualInterestRate() + "balance is " + getBalance();
}
}
- 用户类
package com.ygc.day01_constructor.exerAll_account;
/**
* @author: YGC
* @createTime: 2023/08/25 14:39
* @blogs: <a>https://www.cnblogs.com/ygcDiary</a>
* @description: 用户
* 2、创建Customer类。
* a. 声明三个私有对象属性:firstName、lastName和account。
* b. 声明一个公有构造器,这个构造器带有两个代表对象属性的参数(f和l)
* c. 声明两个公有存取器来访问该对象属性,方法getFirstName和getLastName返回相应的属性。
* d. 声明setAccount 方法来对account属性赋值。
* e. 声明getAccount 方法以获取account属性。
*/
public class Customer {
private String firstName;
private String lastName;
private Account account;
//构造器,构造名字
public Customer(String f, String l) {
firstName = f;
lastName = l;
}
public void setFirstName(String f) {
firstName = f;
}
public String getFirstName() {
return firstName;
}
public void setLastName(String l) {
lastName = l;
}
public String getLastName() {
return lastName;
}
public void setAccount(Account account1) {
account = account1;
}
public Account getAccount() {
return account;
}
}
- 用户测试类
package com.ygc.day01_constructor.exerAll_account;
/**
* @author: YGC
* @createTime: 2023/08/25 14:48
* @blogs: <a>https://www.cnblogs.com/ygcDiary</a>
* @description: 用户测试
* (1)创建一个Customer ,名字叫 Jane Smith, 他有一个账号为1000,余额为2000元,年利率为 1.23% 的账户。
* (2)对Jane Smith操作。
* 存入 100 元,再取出960元。再取出2000元。
* 打印出Jane Smith 的基本信息:
* 成功存入 :100.0
* 成功取出:960.0
* 余额不足,取款失败
* Customer [Smith, Jane] has a account: id is 1000, annualInterestRate is 1.23%, balance is 1140.0
*/
public class CustomerTest {
public static void main(String[] args) {
Customer customer = new Customer("Jane", "Smith");
//创建account
Account account = new Account(100, 2000, 0.0123);
//建立练习account<=>customer
customer.setAccount(account);
//按照实际需求,应该是用户拿着账号取钱
customer.getAccount().deposit(100);
customer.getAccount().withdraw(960);
customer.getAccount().withdraw(2000);
System.out.println(account.customerInfo(customer));
}
}
6. 类中的属性赋值的过程
-
类的属性有哪些赋值方式:
-
① 默认赋值
-
② 显示赋值
-
③ 构造器赋值
-
④ 对象.方法 赋值
-
⑤ 对象.属性赋值
-
顺序是:① - ② - ③ - ④/⑤
- 其中 ① - ② - ③只能执行一次
- ④/⑤可以多次调用执行
7. JavaBean
JavaBean就是一个公共的类,里面含有一个无参构造器,和Get,Set方法。方便后期动态的创建对象
-
所谓JavaBean,是指符合如下标准的Java类:
-
类是公共的
-
有一个无参的公共的构造器
-
有属性,且有对应的get、set方法
-
-
JavaBean举例
package com.ygc.day03_bean;
/**
* @author: YGC
* @createTime: 2023/08/26 0:28
* @blogs: <a href="https://www.cnblogs.com/ygcDiary"></a>
* @description: javaBean
*/
public class JavaBean {
private int id;
private String name;
public JavaBean() {
}
public void setId(int i) {
id = i;
}
public int getId() {
return id;
}
public void setName(String n) {
name = n;
}
public String getName() {
return name;
}
}
8. UML类图
-
在软件开发中,使用
UML类图
可以更加直观地描述类内部结构(类的属性和操作)以及类之间的关系(如关联、依赖、聚合等)。-
+表示 public 类型, - 表示 private 类型,#表示protected类型
-
方法的写法:
方法的类型(+、-) 方法名(参数名: 参数类型):返回值类型 -
斜体表示抽象方法或类。
-