四、面向对象(基础)
4.0 Java学习三条主线
- Java类及类的成员:属性、方法、构造器; 代码块、内部类
- 面向对象的三大特征:封装、继承、多态
- 其它关键字:this、super、static、final、abstract、interface、package、import
4.1 面向过程(POP)与面向对象(OOP)
- 面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做
- 面向对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做
4.2 Java语言基本元素:类和对象
- 类 是对一类事物的描述,是抽象的、概念上的定义
- 对象 是实际存在的该类事物的每个个体,因而也称为实例(instance)
类的成员:1. 属性 = 成员变量 = field = 域、字段
2.方法= 成员方法 = 函数 = method
4.2.1 类和对象的使用
- 创建类、设计类的成员
class Person{
//属性
String name;
int age=1;
boolean isMale;
public void eat(){
吃饭;
}
}
-
创建类的对象
-
通过 “对象.属性” 或 “对象.方法” 调用对象的结构
//创建类的对象
Person p1= new Person();
//调用属性
p1.name="Tom";
pi.isMale="True";
System.out.println("p1.name");
Person p2= new Person();
System.out.println("p2.name");//null
System.out.println("p2.isMale");//false
//把 p1变量 保存的 对象地址值 赋给 p3,导致p1和p3指向了堆空间中同一实体
Person p3 = p1;
System.out.println("p3.name");//Tom
p3.age=10;
System.out.println("p1.age");//10
4.2.2 类的成员之一:属性
属性(成员变量) 与 局部变量
-
相同点
1.1 定义变量的格式: 数据类型 变量名 = 变量值
1.2 先声明,后使用
1.3 变量都有其作用域
-
不同点
2.1 声明位置不同
属性:直接定义在类的{}中
局部变量:声明在 方法内、方法形参、代码块内、构造器形参、构造器内部 的变量
2.2 在内存中加载的位置:
属性:加载到 堆空间 中(非static)
局部变量:加载到 栈空间
2.3 权限修饰符的不同
属性:可以在声明属性时,制定其权限、使用权限修饰符。
常用权限修饰符:private、public、缺省、protected
2.4 生命周期不同
2.5 默认初始化值情况
属性:类的属性,根据其类型,都有默认初始化值。
整型(byte、short、int、long):0
浮点型(float、double):0.0
字符型(char):0
布尔型(boolean):false
引用数据类型(类、数组、接口):null
局部变量:没有默认初始化值—在调用局部变量时,一定要显式赋值。
特别的,形参在调用时赋值即可。
4.2.3 类的成员之二:方法
方法:描述类应该具有的功能。
方法声明:
权限修饰符 返回值类型 方法名(形参列表){
方法体
}
注:由 static、final、abstract来修饰的方法,后面再讲。
return关键字使用:
-
使用范围:在方法体中。
-
作用:①结束方法
②有返回值的方法,使用"return 数据"返回数据
4.3 JVM内存结构
《JVM规范》
编译完源程序后,生成一个或多个字节码文件。
我们使用JVM中的类的加载器和解释器对生成的字节码文件进行解释运行。意味着,需要将字节码文件对应的类加载到内存中,涉及到内存解析。
Java中内存结构划分为:虚拟机栈、堆、方法区;程序计数器、本地方法栈。
- 虚拟机栈:以 栈帧 为基本单位,有入栈和出栈操作;每个栈帧入栈操作对应一个方法的执行;方法内的 局部变量 存放在 栈帧 中。
-
堆:我们将 new出来的结构(数组,对象)加载在 堆空间 中:
- 数组,数组的元素在堆中
- 对象的成员变量在堆中
- 补充:对象的属性(非static)加载在 堆空间 中。
-
方法区:加载的类的模板信息、常量池、静态域。
4.4 变量分类
4.4.1 按数据类型
引用数据类型:类、数组、接口、枚举、注解、记录。
4.4.2 按在类中声明的位置
注:引用类型的变量,只可能存储两类值: null 或 地址值(含变量的类型)
4.5 匿名对象
-
理解:
我们创建的对象,没有显式的赋给一个变量名,即为匿名对象。
-
特征:
匿名对象只能调用一次。(匿名对象一经使用,就被回收)
-
使用:
代码块 line:18-20
public class InstanceTest {
public static void main(String[] args){
Phone p = new Phone();
System.out.println(p);
p.sendEmail();
p.playGame();
//匿名对象的使用
new Phone().sendEmail();
new Phone().playGame();
new Phone().price=1999;
new Phone().showPrice();//0.0
//****************************
PhoneMall mall = new PhoneMall();
//匿名对象的使用
mall.show(new Phone());
//匿名对象(作为实参)赋给形参,相当于赋给一个有名字的对象
}
}
class PhoneMall{
public void show(Phone phone){
phone.sendEmail();
phone.playGame():
}
}
class Phone{
double price;//价格
public void sendEmail(){
}
public void playGame(){
System.out.println("打游戏");
}
public void showPrice(){
System.out.println("手机价格为"+price);
}
}
4.6 再谈方法
4.6.1 方法的重载(Overload)
- 定义:
1. 同一个类中,允许存在 同名方法 ,但 参数个数 或 参数类型 不同。
2. 与 返回值类型 无关,只看参数列表,且参数列表(参数个数 或 参数类型)必须不同。
3. 调用时,根据 方法参数列表的不同 来区别。
“两同一不同”:- 同一个类,相同方法名
- 参数列表不同:参数个数不同,参数类型不同
-
举例:
Arrays类中重载的sort()/binarySearch()
-
判断是否是重载:
跟方法的 权限修饰符、返回值类型、形参变量名、方法体 都没有关系!
-
在通过对象调用方法时,如何确定一个指定的方法:
方法名 ----> 参数列表
4.6.2 可变形参的方法
Varargs(variable number of arguments):可变个数的形参,允许直接定义能和多个实参相匹配的形参。
-
JDK 5.0 新增的内容
-
具体使用:
2.1 可变个数形参的格式: 数据类型 ... 变量名
2.2 当调用可变个数形参的方法时,传入的参数个数可以是:0个,1个,2个,多个。
2.3 可变个数形参方法与本类中方法名相同,形参不同的方法之间构成重载。
2.4 数据类型 ... 变量名 与 数据类型[] 变量名 不构成重载,即不可共存。
2.5 可变个数形参 在方法的形参中,必须声明在末尾,且最多只能声明一个可变形参。
public class MethodArgsTest{
public static void main(String[] args){
MethodArgsTest test = new MethodArgsTest();
test.show(12);
test.show("hello");//show(String)
test.show("hello","world");//show(String ... strs) 多个形参
test.show();//show(String ... strs) 0个形参
test.show(new String[]{"AA","BB","CC"});//show(String ... strs) 传 数组
}
public void show(int i){
}
public void show(String s){
System.out.println("show(String)");
}
public void show(String ... strs){
System.out.println("show(String ... strs)");
//遍历,即把 strs 当做 数组
for(int i=0;i<strs.length;i++){
System.out.println(strs[i]);
}
}
//可变参数形参,必须声明在末尾
public void show(int i,String ... args){
}
}
4.6.3 方法参数的值传递机制☆
1. 关于变量的赋值:
如果变量是基本数据类型,此时赋值的是 变量所保存的数据值。
如果变量是引用数据类型,此时赋值的是 变量所保存的数据的地址值。
public class ValueTransferTest{
public static void main(String[] args){
System.out.println("*************基本数据类型:*************");
int m = 10;
int n = m;
System.out.println("m = "+ m + ", n = "+n);//m = 10, n = 10
n = 20;
System.out.println("m = "+ m + ", n = "+n);//m = 10, n = 20
System.out.println("*************引用数据类型:*************");
Order o1 = new Order();
o1.orderId = 1001;
Order o2 = o1;//把 o1 的 地址值 赋给 o2
System.out.println("o1.orderId = "+ o1.orderId +
", o2.orderId = "+ o2.orderId);//1001 1001
o2.orderId = 1002;
System.out.println("o1.orderId = "+ o1.orderId +
", o2.orderId = "+ o2.orderId);//1002 1002
}
}
class order{
int orderId;
}
2. 方法的形参的传递机制:值传递
若参数是基本数据类型,则实参传递给形参的是 实参真实存储的数据值;
若参数是引用数据类型,则实参传递给形参的是 实参存储数据的地址值。
public class ValueTransferTest2{
public static void main(String[] args){
Data data = new Data();
data.m=10;
data.n=20;
System.out.println("m = "+ m + ", n = "+n);//m=10 n=20
ValueTransferTest2 test =new ValueTransferTest2();
test.swap(data);//传地址
System.out.println("m = "+ m + ", n = "+n);//m=20 n=10
}
public void swap(Data data){
int temp=data.m;
data.m=data.n;
data.n=temp;
}
}
class Data{
int m;
int n;
}
P211
4.6.4 递归方法(Recursion)
1、何为递归?——方法自己调用自己。
2、递归分类:直接递归,间接递归。
3、说明:
- 递归方法包含了一种“隐式循环”
- 递归方法会“重复执行”某段代码,但这种重复无须循环控制
- 递归一定要向“已知方向“递归,否则这种递归就成了无穷递归,类似于”死循环“,最终导致”栈内存溢出“
4.7 package与import关键字使用
4.7.1 package关键字的使用
1、说明
package 包名.子包名
- package:包 (所在公司域名倒置:com.atguigu.xxx 不能 java.xx)声明在首行
- package用于指定文件中定义的类、接口等结构所在的包
- package作为Java源文件的第一条语句出现;若缺省,则指定为无名包。
- 包对应文件系统的目录,用“.”来指明包(目录)的层次
- 包名,属于标识符,满足标识符命名的规则和规范(全部小写)
- 同一个包下可声明多个结构(类、接口),但不能定义同名结构
2、包的作用
- 包可以包含 类 和 子包 , 划分项目层次,便于管理
- 帮助管理大型软件系统(如 MVC设计模式:视图模型Viewer层,控制器Controller层,数据模型Model层)
- 解决类命名冲突问题
- 控制访问权限
3、JDK中主要的包
java.lang
----Java语言核心类,如String、Math、Integer、System、Thread
java.net
----包含执行与网络相关的雷和接口
java.io
----包含能提供多种输入、输出功能的类
java.util
----包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数
java.text
----包含一些java格式化相关的类
java.sql
----包含了java进行JDBC数据库编程的相关类、接口
java.awt
----包含了构成抽象窗口工具集(abstract window toolkits)的多个类
4.7.2 import关键字的使用
import:导入 类
import 包名.类名
-
import声明在包的声明和类的声明之间
-
若使用 “ a.* ” 导入结构,则可以使用 a包下的所有类
-
若是 lang包 下的类,或是 当前包 下的,则可 省略import
-
如果已经导入 java.a包 下的类,那么如果使用 a包的子包 下的类,仍然需要导入
-
使用不同包下的同名的类:使用全类名的方式
import java.util.Date; import java.sql.Date; //Date可以使用import方式指明 Date date = new Date(); //使用全类名的方式 java.sql.Date date1 = new java.sql.Date(121212);
-
import static
组合的使用:调用指定类或接口下的静态的属性或方法import static java.lang.System.out; out.println("hello");
4.8 类的成员之三:构造器(Constructor)
4.8.1 构造器的作用
- 搭配new关键字,创建类的对象
- 在创建对象的同时,给对象的相关属性赋值(初始化对象的成员变量)
4.8.2 构造器使用
- 声明格式: 权限修饰符 类名(形参列表){}
- 创建类以后,在没有显示提供任何构造器的情况下,系统会默认提供一个 空参的构造器,且构造器的权限与类声明的权限相同。
- 一旦类中显示声明了构造器,则系统不再提供 默认的空参的构造器。
- 一个类中可以声明多个构造器,彼此之间构成重载。
4.8.3 问题
-
构造方法和普通方法的区别
编写代码的角度:没有共同点。声明格式、作用都不同。
字节码文件角度:构造器会以
<init>()方法
的形态呈现,用以初始化对象。
4.9 面向对象特征之一:封装性
4.9.1 为什么需要封装性?
面向对象开发原则:“高内聚、低耦合”
内聚:指一个模块内各个元素彼此结合的紧密程度。(重用,独立)
耦合:之一个软件结构内不同模块之间互联程度的度量。(牵一发而动全身)
理论上:
高内聚:类的内部数据操作自己完成,不允许外部干涉;
(Java程序通常以类的形态呈现,相关的功能封装到方法中)
低耦合:仅暴露少量的方法给外部使用,尽量方便外部调用。
(给相关的类、方法设置权限,把该隐藏的隐藏起来,该暴露的暴露出去)
通俗来说:
把该隐藏的隐藏起来,把该暴露的暴露出去。
4.9.2 如何体现数据封装?
- 权限修饰符
java规定了4种权限修饰,分别是:private、缺省、protected、public
- 作用(什么是封装性?)
我们可以用4种权限修饰来修饰类及类的内部成员,当这些成员被调用时,体现可见性的大小。
- 使用
package com.atguigu08.constructor;
- 权限修饰符访问范围
- 实现封装 就是 控制类或成员的可见性范围 ,这就需要依赖权限修饰符(访问修饰符)来控制。
修饰符 | 本类内部 | 本包内 | 其他包的子类 | 其他包非子类 |
---|---|---|---|---|
private | √ | × | × | × |
缺省 | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
- 具体修饰的结构:
- 外部类:public、缺省
- 成员变量、成员方法、构造器、成员内部类:public、protected、缺省、private
![image-20230322170700653](封装性.assets/image-20230322170700653.png)
- 开发中4种权限使用频率:
- 比较高:public、private
- 比较低:缺省、protected
4.9.3 封装性的体现
- 场景1:私有化(private)类的属性,提供公共(public)的get和set方法,对此属性进行获取或修改
- 场景2:将类中不需要对外暴露的方法,设置为private
- 场景3:单例模式中构造器private的,避免在类的外部创建实例(在static关键字后讲)
4.9.4 问题
-
main方法的public能不能换成private?为什么?
能。但换成private之后就不能作为程序的入口了,就只是一个普通的方法。
4.10 阶段性知识补充
4.10.1 类中属性赋值过程
1、赋值方式:
-
默认初始化
-
显式初始化
-
构造器中初始化
-
通过“对象.方法”的方式赋值
-
通过“对象.属性”的方式赋值
2、赋值顺序:
① - ② - ③ - ④/⑤
3、以上操作在对象创建过程中可以执行多少次
- 只能执行1次:①②③
- 可以多次执行:④⑤
4.10.2 JavaBean
- JavaBean是一种Java语言写成的可重用组件。
- 所谓JavaBean,是指符合如下标准的Java类:
- 类是公共的
- 有一个无参的共同的构造器
- 有属性,且有对应的get、set方法
4.10.3 UML类图
UML(Unified Modeling Language,统一建模语言),用来描述软件模型和架构的图形化语言。