JAVA类与面向对象
JAVA | 类与面向对象
day8
对于任何的知识点我们都可以从这三个点去剖析: 3W => what? why? how?
面向过程与面向对象
Java面向对象学习的三条主线
- Java类及类的成员
- 属性、方法、构造器;代码块、内部类
- 面向对象的三大特征
- 封装性、继承性、多态性
- 其他关键字
- this、super、static、final、abstract、interface、package、import等
面向过程(POP)与面向对象(OPP)
- 两者都是一种思想,面向对象是相对于面向过程而言的。面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做。面向对象,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做
- 面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如抽象、分类、继承、聚合、多态等
- 面向过程:Procedure Oriented Programming
- 面向对象:Object Oriented Programming
面向对象的三大特征
- 封装
- 继承
- 多态
面向对象的思想概述
- 程序员从面向过程的执行者转化成了面向对象的指挥者
- 面向对象分析方法分析问题的思路和步骤
- 根据问题需要,选择问题所正对的现实世界中的实体
- 从实体中寻找解决问题相关的属性和国内,这些属性和功能就形成了概念世界中的类
- 把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义。即借助某种程序语言,把类构造成计算机能够识别和处理的数据结构
- 将类实例化成计算机世界中的对象。对象是计算机世界中解决问题的最终工具
java语言的基本元素:类和对象
- 类和对象是面向对象的核心概念
- 类是对一类事物的描述,是抽象的、概念上的定义
- 对象是实际存在的该类事物的每个个体,因而也称为实例(instance)
- 万事万物皆对象
- Java面向对象学习的三条主线
- Java类及类的成员:属性、方法、构造器;代码块、内部类
- 面向对象的三大特征:封装性、继承性、多态性、[抽象性]
- 其他关键字:this、super、static、final、abstract、interface、package、import等
- "人把大象装进冰箱"的举例
- 面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做
- 把冰箱门打开
- 抬起大象,塞进冰箱
- 把冰箱门关闭
- 面向对象强调具备了功能的对象,以类/对象为最小单位,考虑谁来做
人{ 打开(冰箱){ 冰箱.打开(); } 抬起(大象){ 大象.进入(冰箱); } 冰箱(冰箱){ 冰箱.闭合(); } } 冰箱{ 打开(){} 闭合(){} } 大象{ 进入(冰箱){} }
- 面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做
- 面向对象的两个要素
- 类:是对一类事物的描述,是抽象的、概念上的定义
- 对象:是实际存在的该类事物的每个个体,因而也称为实例(instance)
- 面对对象程序设计的重点是类的设计
- 设计类,就是设计类的成员
设计类,就是设计类的成员
-
定义
- 属性 = 成员变量 = field = 域、字段
- 方法(行为) = 成员方法 = 函数 = method
- 创建类的对象 = 类的实例化 = 实例化类
-
类和对象的使用(面向对象思想落地的实现)
- 创建类,设计类的成员
- 创建类的对象
- 通过
对象.属性
或对象.属性
的方式来调用对象的结构
-
如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性(非static的)
- 意味着,如果我们修改一个对象的属性a,则不影响
-
对象的内存解析
- 堆,此内存区域的为宜目的就是存放对象实例
- 通常所说的栈(stack),是指虚拟机栈。虚拟机栈用于存储局部变量等
- 方法区(Method Area),用于存储已被虚拟机加载的类信息、常量、静态变量、即使编译器编译后的代码等数据
-
对象的内存解析
Person p1 = new Person(); p1.name = "Tom"; p1.isMale = treue; Person p2 = new Person(); System.out.println(p2.name);//null Person p3 = p1; p3.age = 10;
类中属性的使用
- 属性(成员变量) vs 局部变量
- 相同点
- 定义变量的格式:数据类型 变量名 = 变量值
- 先声明,后使用
- 变量都有其对于的作用域,超过作用域范围都会失效
- 不同点
- 在类中声明的位置不同
- 属性:直接定义在类的一对{}内
- 局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
- 关于权限修饰符的不同
- 属性:可以在声明属性时,指明其权限,使用权限修饰符
- 常用的权限修饰符:private、public、缺省(不写即缺省)、protected =>(属于封装性的内容)
- 目前学习时,声明属性,使用缺省即可
- 局部变量:不可以使用权限修饰符
- 属性:可以在声明属性时,指明其权限,使用权限修饰符
- 在类中声明的位置不同
- 关于默认初始化的情况
- 属性:类的属性,根据其类型,都有默认初始化值
- 整形(byte、short、int、long),0
- 浮点型(float、double),0.0
- 字符型(char),0(或'\u0000')
- 布尔型(boolean),false
- 引用数据类型(类、数组、接口),null
- 局部变量:没有默认初始化值
- 意味着,我们在调用局部变量之前,一定要显式赋值
- 特别的,形参在调用时,赋值即可
- 属性:类的属性,根据其类型,都有默认初始化值
- 在内存中加载的位置
- 属性:加载到堆空间中(非static)
- 局部变量:加载到栈空间
- 相同点
类中方法的声明和使用
-
方法:描述类应该具有的功能
-
比如:
- Math类:sqrt(), random()...
- Scanner类:nextXxx()...
- Arrays类:sort(), binarySearch(), toString(), equals()...
-
举例:
public void eat(){} public void sleep(int hour){} public String getName(){} public String getNation(String nation){}
-
方法的声明
权限修饰符 返回值类型 方法名([形参列表]){ 方法体; }
- 关于static、final、abstract修饰的方法,后面涉及
-
说明
- 关于权限修饰符: 默认方法的权限修饰符等详细讲解前,都使用public
- java规定的4种权限修饰符:private、public、缺省、protected=>(属于封装性的内容)
- 返回值类型
- 有返回值 vs 无返回值
- 如果方法有返回值,则必须在方法声明时,指定返回值的类型。
- 需要使用return关键字来指定类型的变量或常量(数据)
- 如果方法没有返回值,则方法声明时,使用void来表示
- 通常没有返回值的方法中,就不需要使用return,如果要使用,只能"return;"表示结束此方法的意思,且后面不可以声明表达式
- 如果方法有返回值,则必须在方法声明时,指定返回值的类型。
- 定义方法时该不该有返回值
- 看题目要求
- 凭经验,具体问题具体分析
- 有返回值 vs 无返回值
- 方法名:属于标识符,遵循标识符的规则和规范【见名知意】
- 形参列表:方法可以声明0个,1个或多个对象
- 格式:
数据类型1 形参1, 数据类型2 形参2, ...
- 定义方法时,该不该定义形参?
- 看题目要求
- 凭经验,具体问题具体分析
- 格式:
- 方法体:方法功能的体现
- 关于权限修饰符: 默认方法的权限修饰符等详细讲解前,都使用public
-
-
return关键字的使用
- 使用返回:使用在方法体中
- 作用:
- 结束方法
- 针对于有返回值类型的方法,使用
return 数据
方法返回所要的数据 - 注意点:return关键字后面不能声明执行语句
-
方法的使用中,可以调用当前类的属性或方法(方法内部可以调用方法
- 特殊的,方法A中又调用了方法A:递归方法
- 方法中,不可以定义方法(public方法中套public方法)
-
方法的分类:按照是否有形参及返回值
无返回值 | 有返回值 | |
---|---|---|
无形参 | void 方法名(){} | 返回值的类型 方法名(){} |
有形参 | void 发放名(形参列表){} | 返回值的类型 方法名(形参列表){} |
匿名对象的使用
-
理解,创建的对象,没有显式的赋给一个变量名,即为匿名对象
-
特征,匿名对象只能调用一次
-
使用
public class InstanceTest { public static void main(String[] args) { Phone p = new Phone(); // p = null; 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(p); //匿名对象的使用,一般s mall.show(new Phone()); } } class PhoneMall{ public void show(Phone phone){ phone.sendEmail(); phone.playGame(); } } class Phone{ double price;//价格 public void sendEmail(){ System.out.println("发送邮件"); } public void playGame(){ System.out.println("玩游戏"); } public void showPrice(){ System.out.println("手机价格为:" + price); } }
考察
面向对象的编程思想(类、对象:三大特征...)万事万物皆对象
-
面对对象思想编程内容的三条主线分别是什么
- 类及类的成员:属性、方法、构造器、代码块、内部类
- 面向对象的三大特征:封装、继承、多态
- 其他关键字:this, super, abstract, interface, static, final, package, import
-
谈谈你对面向对象中类和对象的理解,并指出二者的关系
- 类:抽象的、概念上的内容
- 对象:实实在在存在的一个个体
- 二者的关系
- 对象是由类派生出来的
-
面向对象思想的体现:类和对象的创建和执行操作有哪三步
- 创建类
- 类的实例化
- 调用对象的结构:"对象.属性", "对象.方法"
-
内存分配情况
-
类的方法内是否可以定义变量?是否可以调用属性?是否可以定义方法?是否可以调用方法?
- 类的方法内可以定义变量,可以调用属性,不可以定义方法,可以调用方法
day09
理解“万事万物皆对象”
- 在java语言范畴中,我们都将概念、结构等封装到类中,通过类的实例化,来调用具体的功能结构
- Scanner, String等
- 文件,File
- 网络资源,URl
- 涉及到java语言与前端Html、后端的数据库交互时,前后端的结构在java层面交互时,都体现为类、对象
再谈方法
- 方法的重载
- 可变形参的方法
- ⭐方法参数的值传递机制
- 递归方法
方法的重载(overload)
- 概念
- 重载的概念
- 在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或参数类型不同即可
- 重载的特点
- 与返回值类型无关,只看参数列表,且参数列表必须不同(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别
- 重载示例
//返回两个整数的和 int add(int x,int y){return x+y;} //返回三个整数的和 int add(int x,int y,int z){return x+y+z;} //返回两个小数的和 double add(double x,double y){return x+y;}
- 定义:在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或参数类型不同即可
- 两同一不同
- 同一个类,相同方法名
- 参数列表不同
- 参数个数
- 参数类型不同
- 两同一不同
- 举例:Arrays类中重载的sort() / binarySearch()
- 判断是否是重载
- 个方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系
- 在通过对象调用方法时,如何确定某一个指定的方法
- 先看
方法名
同时再看参数列表
- 先看
public class OverLoadTest {
public static void main(String[] args) {
OverLoadTest test = new OverLoadTest();
//默认匹配1,如果将1注释掉,就会将int匹配double输出2
test.getsum(1,2);
test.getsum(1.1,2.2);
}
//如下的两个同名方法构成了重载
public void getsum(int i,int j){
System.out.println(1);
}
public void getsum(double d1,double d2){
System.out.println(2);
}
public void getsum(String s, int i){
System.out.println(3);
}
public void getsum(int i,String s){
System.out.println(4);
}
//不允许重复的重载
// public void getsum(int i,int j){
// return 0;
// }
//即使参数名不同但类型相同也不行,返回的类型也不影响
// public int getsum(int m,int n){
//
// }
//权限不同也不行
// private void getsum(int i,int j){
//
// }
}
- 使用重载方法,可以为编程带来方便,实际上我们调用System.out.println()方法,其内部也是有许多写好的重载
- 练习
可变个数的形参
- 概念
-
javaSE 5.0中提供了Varargs机制, 允许直接定义能和多个实参相匹配的形参。从而可以用一种更简单的方式,来传递个数可变的实参
-
jdk5.0之前
public void show(String ... strs){}
-
jdk5.0之后
public void show(String[] strs){}
-
- jdk 5.0新增的内容
- 具体的使用
- 可变个数形参的格式:
数据类型 ... 变量名
- 当调用可变个数形参的方法时,传入的参数个数可以时:0个,1个,2个...
- 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
- 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重构,换句话说两者之间不能共存
- 可变个数形参在方法的形参中,必须声明在末尾
- 可变个数形参在方法的形参中,最多只能声明一个可变形参
- 可变个数形参的格式:
public class MethodArgsTest {
public static void main(String[] args) {
MethodArgsTest test = new MethodArgsTest();
test.show(1);//int i
// test.show("hello");//String ... strs
// test.show("hello", "world");//String ... strs
// test.show();//String ... strs
//jdk5.0之后(可兼容下面两种方法)
test.show("AA","BB","CC");
//jdk5.0之前(只可以使用下面这种方法)
// test.show(new String[]{"AA","BB","CC"});
}
public void show(int i){
System.out.println("int i");
}
// public void show(String s){
// System.out.println("String s");
// }
//jdk5.0之后:可变个数形参
public void show(String ... strs){
System.out.println("String ... strs");
for(int i = 0;i < strs.length;i++){
System.out.println(strs[i]);
}
}
//jdk5.0之前:可变个数形参
// public void show(String[] strs){
// System.out.println("String[] strs");
// for(int i = 0;i < strs.length;i++){
// System.out.println(strs[i]);
// }
// }
//---------------------
//可变个数形参不能写在前面
//报错信息:The variable argument type String of the method
//报错信息:show must be the last parameter
// public void show(String ...strs,int i){
//
// }
//可变个数形参只能写在末尾
// public void show(int i, String ...strs){
//
// }
}
⭐方法参数的值传递机制
- 概念
-
方法:必须由其所在类或对象调用才有意义。若方法含有参数
-
形参:方法声明时的参数
-
实参:方法调用时实际传给新参的参数值
-
java的实参值如何传入方法呢?
java内方法的参数传递方法只有一种:值传递。即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响
- 形参时基本数据类型,将实参基本数据类型变量的”数据值“传递给形参
- 形参时引用数据类型,将实参引用数据类型变量的”地址值“传递给形参
-
- JAVA中方法参数的值传递机制
- 如果变量时基本数据类型,此时赋值的是变量所保存的数据值
- 如果变量时引用数据类型,此时赋值的是变量所保存的数据的地址值
- 形参与实参
- 形参:方法定义时,声明的小括号内的参数
- 实参:方法调用时,实际传递给形参的数据
- 值传递机制
- 如果参数是基本数据类型,此时实参赋值给形参的是,实参真实存储的数据值
- 如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值
练习
方法参数值传递机制的例题与图解
- 方法的参数传递
- 此题中有个坑,就是他在method中操作的是形参而非实参,所以照他程序输出只会输出两个10,而非题目所要的100 200,所以这里有两个思路
- 在method里面输出a和b指定的值,然后提前将程序结束
System.exit(0)
- 在method中重写println函数,这里涉及到后面的知识
- 在method里面输出a和b指定的值,然后提前将程序结束
- 此题中有个坑,就是他在method中操作的是形参而非实参,所以照他程序输出只会输出两个10,而非题目所要的100 200,所以这里有两个思路
数组的计算
- 这里也有个坑,如果是按照左边的方式进行计算就会得到错误,因为如果从头开始计算(12),则后面的数使用的除数就会发生变化(1),从而得到错误的答案,这里给出两个解决方案
- 从最后一个数开始计算,直到第一个,第一个数变化了也代表程序结束了,所以不会导致逻辑错误
- 创建一个新的变量存储一开始的第一个值,然后重复的计算(缺点是需要额外开辟一个空间存储新的变量)
关于char型数组的输出
- 这里有个坑是char型输出的不是地址值,而是字符abc,因为在
println
的重载函数中针对char的方法体是遍历这个数组,而非输出地址值
public class ArrayPrintTest {
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
}
}
将对象作为参数传递给方法
- 考察参数的值传递
- 定义一个Circle类,包含一个double型的radius属性代表圆的半径
一个findArea()方法返回圆的面积 - 定义一个类 PassObject,在类中定义一个方法 printAreas(),该方法的定义如下: public void printAreas(Circle c, int time)
在 printareas方法中打印输出1到time之间的每个整数半径值,以及对应的面积
例如, times为5,则输出半径1,2,3,4,5,以及对应的圆面积。 - 在main中调用printAreas()方法,调用完后输出当前半径值
//Circle.java
public class Circle {
double radius;//半径
//求圆的面积
public double findArea(){
return Math.PI * radius * radius;
}
}
//PassObject.java
class PassObject {
public static void main(String[] args) {
PassObject test = new PassObject();
Circle c = new Circle();
test.printAreas(c, 5);
//快捷写法
//test.printAreas(new Circle, 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;
//友好写法
double area = c.findArea();
System.out.println(c.radius + "\t\t" + area);
//快捷写法
//System.out.println(c.radius + "\t\t" + c.findArea());
}
c.radius = time + 1;
}
}
递归方法
-
概念
-
递归方法:一个方法体内部调用他自身
-
方法递归包含了一种隐式的循环,他会重复执行某段代码,但这种重复指向无需循环控制
-
递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环
-
例题
//计算1-100之间所有自然数的和 public int sum(int num){ if(num == 1){ return 1; }else{ return num + sum(num - 1); } }
-
-
递归方法的使用与了解
/*
## 递归方法的使用(了解)
1. 递归方法:一个方法体内部调用他自身
2. 方法递归包含了一种隐式的循环,他会重复执行某段代码,但这种重复指向无需循环控制
- 递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环
*/
public class RecursionTest {
// 例1:计算1-100之间所有自然数的和
public static void main(String[] args) {
//方式一:for循环计算
int sum = 0;
for(int i = 0;i <= 100;i++){
sum += i;
}
System.out.println(sum);
//方式二:递归方法
RecursionTest test = new RecursionTest();
int sum1 = test.getsum(100);
System.out.println(sum1);
//例2求乘积, 若此处数字大,会导致结果溢出!
int sum2 = test.getsum1(5);
System.out.println(sum2);
//例3
int value = test.f(10);
System.out.println(value);
}
//方式二:递归方法
public int getsum(int num){
if(num == 1){
return 1;
}else{
return num + getsum(num - 1);
}
}
// 例2:计算1-100之间所有自然数的乘积:n!
public int getsum1(int num){
if(num == 1){
return 1;
}else{
return num * getsum1(num - 1);
}
}
//例3:已知有一个数列:f(0) = 1,f(1) = 4, f(n+2)=2*f(n+1) + f(n)
//其中n是大于0的整数,求f(10)的值
public int f(int n){
if(n == 0){
return 1;
}else if(n == 1){
return 4;
}else{
// 如果直接调用f(n+2)=2*f(n+1) + f(n),就会栈溢出
// 因为在f(n+2)中永远会往上升导致要求的数是无穷的
// 所以这里应该把+2放到右边
return 2 * f(n - 1) + f(n - 2);
}
}
//例4,斐波那契数列,第一个数是1,后面的一个数等于前两个数之和
//1 1 2 3 5 8 13 21 34 55
//例5,汉诺塔问题
//例6,快排
}
考察
-
什么是方法的重载
-
定义:在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或者参数类型不同即可
-
两同一不同
-
同一个类、相同方法名
-
参数列表不同
-
-
如何调用确定的方法:先看
方法名
同时再看参数列表
-
-
说明Java方法中的参数传递机制的具体体现
-
基本数据类型:数据值
-
引用数据类型:地址值(包含变量的数据类型)
Person p1 = new Person(); User u1 = p1;//编译错误 类型不一致
-
-
成员变量和局部变量在声明的位置是?是否有默认初始化值?是否能有权限修饰符修饰?内存分配的位置上有何不同?
-
谈谈return关键字的使用
- 结束方法
- 针对于有返回值的方法,return+返回数据
-
代码的内存解析
-
需要熟知
- 内存结构:栈(局部变量)、堆(new出来的结构:对象(成员变量)、数组)
- 变量:成员变量与局部变量(方法内、方法形参、构造器内、构造器形参、代码块内)
2021-11-24-day10
面向对象特征之一:封装与隐藏(封装性)
-
为什么需要封装?封装的作用和含义
- 对于外部人员来说,不需要知道内部的逻辑是如何运行的即可使用,就好比开车不需要知道汽车发动机是如何运行的原理那样
-
程序设计追求高内聚,低耦合
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉
- 低耦合:仅对外暴露少量的方法用于使用
-
隐藏对象内部的复杂性,之对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想
-
问题的引入
-
针对于属性体现封装性
-
当我们创建一个类的对象以后,我们可以通过
对象.属性
的方式,对对象的属性进行赋值。这里的赋值操作要受到属性的数据类型和存储范围的制约。但除此之外,没有其他的制约条件。- 但在实际中问题中,加入额外的限制条件。这个条件就不能在属性声明时体现。只能通过方法进行条件的添加。
- 例如下面Animal类中的
setLegs()
- 例如下面Animal类中的
- 但在实际中问题中,加入额外的限制条件。这个条件就不能在属性声明时体现。只能通过方法进行条件的添加。
-
同时,我们需要避免用户再次使用
对象.属性
的方式,对对象的属性进行赋值。则需要将属性声明为私有的(private)
class Animal{ String name; int age; //如果是默认则可以通过外部直接赋值,所以如果要外部不能直接赋值则需要修改其权限修饰符 // int legs;//腿的个数 private int legs; public void setLegs(int l){ //正整数且是偶数 if(l >= 0 && l % 2 ==0){ legs = l; }else{ System.out.println("输入有误!"); legs = 0; //抛出异常(后期内容) } } public void eat(){ System.out.println("动物进食"); } public void show(){ System.out.println("name = " + name + ",age = " + age + ",legs = " + legs); } }
-
-
封装性的体现
- 我们将属性xxx私有化(private),同时提供公共(public)的方法来获取(getXxx)和设置(setXxx)此属性的值
- 拓展:封装性的体现
- 如上
- 不对外暴露的私有方法
- 单例模式
...
-
封装性的体现,需要权限修饰符来配合
-
java规定的4中权限(从小到大):
private、缺省(default)、protected、public
- java权限修饰符置于类的成员定义前,用来限定对象对该类成员的访问权限
修饰符 类内部 同一个包 不同包的子类 同一个工程 private yes (缺省) yes yes protected yes yes yes public yes yes yes yes - 对于class的权限修饰符只可以用public和default(缺省)
- public类可以在任意地方被访问
- default类只可以被同一包内部的类访问
-
4中权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类
-
具体的,4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
- 修饰类只能使用:缺省、public
- 如果要解释的话,因为在外部的类中,如果private了,那就没人可以调用它
- 修饰类只能使用:缺省、public
- 这里只讲述: private、缺省、public这三种权限的区别,关于protected得后面继承的学习之后再讲述
- 这里 private、缺省、public 关于权限的使用,可参考
OrderTest
包中Order
与OrderTest
,OrderTest1
包中OrderTest
这三者的使用OrderTest
包中Order
:可使用private、缺省、public
OrderTest
:可使用缺省、public
OrderTest1
包中OrderTest
:可使用public
-
封装性的总结
- java提供了4种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时的可见性大小
练习
-
创建程序,在其中定义两个类: Person和 PersonTest类。定义如下:用 setAge()设置人的合法年龄(0~130),用 getAge()返回人的年龄。在 PersonTest类中实例化 Person类的对象b,调用 setAge()和getAge()方法,体会Java的封装性。
//Person.java package day10.exer; /* 创建程序,在其中定义两个类: Person和 PersonTest类。 定义如下:用 setAge()设置人的合法年龄(0~130),用 getAge()返回人的年龄。 在 PersonTest类中实例化 Person类的对象b,调用 setAge()和getAge()方法,体会Java的封装性。 */ public class Person { private int age; public void setAge(int a){ if(a < 0 || a > 130){ //后期的抛出异常写法 // throw new RuntimeException("传入的数据非法!"); System.out.println("传入的数据非法!"); return;//return用于结束方法 } age = a; } public int getAge(){ return age; } }
//PersonTest.java package day10.exer; /* 在 PersonTest类中实例化 Person类的对象b,调用 setAge()和getAge()方法,体会Java的封装性。 */ public class PersonTest { public static void main(String[] args) { Person p1 = new Person(); // p1.age = 1;//编译不通过 p1.setAge(12); System.out.println("年龄为:" + p1.getAge()); } }
类的成员之三:构造器(或构造方法)
如果没有显式的定义类的构造器的话,默认系统会提供一个无参的构造器,其权限是开头声明的权限
- 构造器 或构造方法(constructor)
- construct: 建设、建造
- construction: CCB(建行)
- constructor: 建设者
- 构造器的作用
- 创建对象
- 初始化对象的属性(信息)
- 说明
- 如果没有显式的定义类的构造器的话,则系统默认提供一个空参的构造器
- 定义构造器的格式:权限修饰符 类名(形参列表)
- 一个类中定义的多个构造器,彼此构成重载
- 一旦显式的定义了类的构造器之后,系统就不再提供默认的空参构造器
- 一个类中,至少会有一个构造器
- 方法与构造器的区别:方法是有了对象,通过对象去调用这个方法,而构造器是用来创建对象的
public class PersonTest {
public static void main(String[] args) {
//创建类的对象: new + 构造器;
Person p = new Person();
p.eat();
Person p1 = new Person("Tom");
System.out.println(p1.name);
}
}
class Person{
//属性
String name;
int age;
//构造器
public Person(){
System.out.println("Person()...");
}
public Person(String n){
name = n;
}
public Person(String n, int a){
name = n;
age = a;
}
//方法
public void eat(){
System.out.println("人吃饭");
}
public void study(){
System.out.println("人可以学习");
}
}
练习
-
练习2
-
在前面定义的Person类中添加构造器,利用构造器设置所有人的age属性初始化值位18
-
修改上题类和构造器,增加name属性,使得每次创建Person对象的同时初始化对象的age属性值和name属性值
-
-
练习3
-
编写两个类,TriAngle和TriAngleTest,其中TriAngle类中声明私有的底边长base和高height,同时声明公共方法访问私有变量。此外提供类必要的构造器。另一个类中使用这些公共方法,计算三角形的面积
//TriAngle.java package day10.exer1; /* triangle:三角形 angle: 角 angel: 天使 编写两个类,TriAngle和TriAngleTest, 其中TriAngle类中声明私有的底边长base和高height,同时声明公共方法访问私有变量。 此外提供类必要的构造器。另一个类中使用这些公共方法,计算三角形的面积 */ public class TriAngle { private double base;//底边长 private double height;//高 public TriAngle(){ } public TriAngle(double b,double h){ base = b; height = h; } public void setBase(double b){ base = b; } public double getBase(){ return base; } public void setHeight(double h){ height = h; } public double getHeight() { return height; } }
//TriAngleTest.java package day10.exer1; public class TriAngleTest { public static void main(String[] args) { TriAngle t1 = new TriAngle(); t1.setBase(2.0); t1.setHeight(2.4); System.out.println("base : " + t1.getBase() + ", height: " + t1.getHeight()); TriAngle t2 = new TriAngle(5.1, 5.6); System.out.println("base : " + t2.getBase() + ", height: " + t2.getHeight()); System.out.println(t2.getBase() * t2.getHeight() / 2); } }
-
属性赋值的先后顺序
- 默认初始化值
- 显式初始化
- 构造器中初始化(赋值
- 通过
对象.方法
或对象.属性
的方法赋值(可以反复执行
- 以上四种操作的先后顺序:1 -> 2 -> 3 -> 4
public class UserTest {
public static void main(String[] args) {
User u = new User();
System.out.println(u.age);
//这里输出的是2,即表示构造器赋值覆盖了显式初始化
User u1 = new User(2);
//这里输出的是3,即表示`对象.方法`覆盖了构造器赋值
u1.setAge(3);
System.out.println(u1.age);
}
}
class User{
String name;
int age = 1;
public User(){
}
public User(int a){
age = a;
}
public void setAge(int age) {
this.age = age;
}
}
扩展知识 - JavaBean
- JavaBean是一种java语言写成的可重用组件
- 所谓JavaBean,是指符合如下标准的java类
- 类是公共的
- 有一个无参的公共的构造器
- 有属性,且有对于的get、set方法
- 用户可以使用 Java Bean:将功能、处理、值、据库访问和其他任何可以用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面、 Servlet、其他 Java Bean、 applet程序或者应用来使用这些对象。用户可以认为 Java Bean提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变。
扩展知识 - UML类图
Account
表示类名(第一行框框)-balance:double
属性(第二行框框)-
表示private类型balance
表示属性名double
表示数据类型
+Account(init_balance:double)
构造器(第三行框框)+
表示public- 括号内的时传参,分别为
传参名 : 传参类型
+getBalance():double
方法(第三行框框)+
表示public():
后面的double
为返回值类型
+vithdraw(eat: double)
方法(第三行框框)+
表示public(eat: double)
表示参数名: 参数类型
- 由于后面是空的所以返回值类型为
void
- 方法的写法:
方法的类型(+、-) 方法名(参数名: 参数类型): 返回值类型
关键字:this的使用
- 概念
- 在Java中,this关键字比较难理解,它的作用和其词义很接近。
- 它在方法内部使用,即这个方法所属对象的引用
- 它在构造器内部使用,表示该构造器正在初始化的对象。
- this表示当前对象,可以调用类的属性、方法和构造器
- 什么时候使用this关键字呢?
- 当在方法内需要用到调用该方法的对象时,就用this.
- 具体的:我们可以用this来区分局部变量和属性
- 比如: this name=name;
- this可以用来修饰、调用:属性、方法、构造器
- this修饰属性和方法:
- this理解为:当前对象或当前正在创建的对象
- 在类的方法中,我们可以使用
this.属性
或this.方法
的方式调用当前对象属性或方法。- 但通常情况下我们一般都选择省略
this.**
- 特殊情况下,如果方法的形参与类的属性同名时,则必须显式的使用
this.**
的方式表明此变量是属性,而非形参
- 但通常情况下我们一般都选择省略
- 在类的构造器中,我们可以使用
this.属性
或this.方法
的方式调用正在创建的对象属性或方法。- 但通常情况下我们一般都选择省略
this.**
- 特殊情况下,如果构造器的形参与类的属性同名时,则必须显式的使用
this.**
的方式表明此变量是属性,而非形参
- 但通常情况下我们一般都选择省略
- this调用构造器
- 我们在类的构造器中,可以显式的使用
this(形参列表)
方式,调用本类中指定的其他构造器 - 构造器中不能通过
this(形参列表)
方式调用自己 - 如果一个类中有n个构造器,则最多有n-1个构造器使用
this(形参列表)
方式(因为不能环回的调用,不然会死循环) - 规定:
this(形参列表)
必须声明在当前构造器的首行且不能使用多个,否则会报错 - 构造器内部,最多只能声明一个
this(形参列表)
用来调用其他的构造器
- 我们在类的构造器中,可以显式的使用
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.setAge(1);
System.out.println(p1.getAge());
p1.eat();
System.out.println("-----------");
Person p2 = new Person("Tom", 21);
System.out.println(p2.getAge());
}
}
class Person{
private String name;
private int age;
//假设现在有新需求,需要在构造器中写出40行代码,那如果4个构造器全部都需要写就太冗余了
//所以可以使用this方法来调用已写了的构造器从而减少冗余
public Person(){
//注意这里不能使用这个,不然会陷入死循环,自己调用自己
//this();
String info = "假设Person初始化时需要考虑1、2、3、4...(共40行代码)";
System.out.println(info);
study();
}
public Person(String name){
//可以使用this()方式调用上面的构造器,然后再执行this下面的代码,注意必须放首行
this();
this.name = name;
}
public Person(int age){
this();
eat();
this.age = age;
}
public Person(String name,int age){
//通过调用第三个构造器(调用第一个构造器),从而实现下面两行注释的过程
this(age);
this.name = name;
//this.age = age;
//Person初始化时需要考虑1、2、3、4...(共40行代码)
}
public String getName() {
return name;
}
public int getAge() {
// return this.age;
return age;
}
public void setName(String name){
//如果传入的参数与类的属性不重名,this可忽略
//name = name//但如果是重名的情况,像这里的操作其实是传入的name自己赋值给自己,没有更改到类的属性
//this: 当前对象
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public void eat(){
System.out.println("人吃饭");
this.study();
}
public void study(){
System.out.println("人学习");
}
}
练习
- 添加必要的构造器,综合应用构造器的重载,this关键字
//Boy.java
package day10.exer2;
public class Boy {
private String name;
private int age;
public Boy() {
}
public Boy(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void marry(Girl girl){
System.out.println("我想娶" + girl.getName());
}
public void shout(){
if(this.age >= 22){
System.out.println("可以合法登记结婚!");
}else{
System.out.println("再多谈一下恋爱先~");
}
}
}
//Girl.java
package day10.exer2;
public class Girl {
private String name;
private int age;
public Girl() {
}
public Girl(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void marry(Boy boy){
System.out.println("我想嫁给" + boy.getName());
//需要传入girl(当前对象),而不是name或者age参数
boy.marry(this);
}
/**
* @Description 比较两个对象的大小
* @author bit
* @date 2021.11.22 21:20
* @param girl
* @return 正数:当前对象大;负数:当前对象小;0:当前对象与形参对象相等
//引申:对象排序,一般是参考对象里面的属性来排序
*/
public int compare(Girl girl){
// if(this.age > girl.age){
// return 1;
// }else if(this.age < girl.age){
// return -1;
// }else{
// return 0;
// }
return this.age - girl.age;
}
}
//BoyGirlTest.java
package day10.exer2;
public class BoyGirlTest {
public static void main(String[] args) {
Boy boy = new Boy("罗密欧", 21);
boy.shout();
Girl girl = new Girl("朱丽叶", 18);
girl.marry(boy);
Girl girl1 = new Girl("祝英台", 19);
int compare = girl.compare(girl1);
if(compare > 0){
System.out.println(girl.getName() + "大");
}else if(compare < 0){
System.out.println(girl1.getName() + "大");
}else{
System.out.println(girl.getName() + "与" + girl1.getName() + "一样大");
}
}
}
实验一
模拟用户与账户之间的存取款操作
//Account.java
package day10.Account_Customer;
public class Account {
private int id;
private double balance;
private double annuallnterestRate;
/**
* @param id 账号
* @param balance 余额
* @param annuallnterestRate 年利率
*/
public Account(int id, double balance, double annuallnterestRate) {
this.id = id;
this.balance = balance;
this.annuallnterestRate = annuallnterestRate;
}
public int getId() {
return id;
}
public double getBalance() {
return balance;
}
public double getAnnuallnterestRate() {
return annuallnterestRate;
}
public void setId(int id) {
this.id = id;
}
public void setBalance(double balance) {
this.balance = balance;
}
public void setAnnuallnterestRate(double annuallnterestRate) {
this.annuallnterestRate = annuallnterestRate;
}
/**
* @description: 取钱
* @param amount
* @author bit
* @date 2021/11/23 14:00
*/
public void withdraw(double amount) {
if (balance >= amount) {
balance -= amount;
System.out.println("成功取出!" + amount + ", 剩余余额为:" + balance);
} else {
System.out.println("余额不足!取款失败");
}
}
/**
* @description: 存钱
* @param amount
* @author bit
* @date 2021/11/23 13:53
*/
public void deposit(double amount) {
if (amount > 0){
balance += amount;
System.out.println("成功存入:" + amount + ", 剩余余额为:" + balance);
}
}
}
//Customer.java
package day10.Account_Customer;
public class Customer {
private String firstName;
private String lastName;
private Account account;
public Customer(String f, String l) {
this.firstName = f;
this.lastName = l;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
}
//CustomerTest.java
package day10.Account_Customer;
public class CustomerTest {
public static void main(String[] args) {
Customer c1 = new Customer("Jane", "Smith");
Account account = new Account(1000, 2000, 0.0123);
c1.setAccount(account);
c1.getAccount().deposit(100);
c1.getAccount().withdraw(960);
c1.getAccount().withdraw(2000);
System.out.println("Customer [" + c1.getLastName() + ", " + c1.getFirstName() + "] has a account: id is " +
c1.getAccount().getId() + ", annuallnterestRate is " + c1.getAccount().getAnnuallnterestRate() * 100 +
"%, balance is " + c1.getAccount().getBalance());
}
}
实验二
基于实验一增加银行,通过银行来对账户与用户操作
//Customer.java
package day10.Account_Customer_Bank;
/**
* @description: 用户类
* @author bit
* @date 2021/11/23 14:56
* @version 1.0
*/
public class Customer {
private String firstName;
private String lastName;
private Account account;
public Customer(String f, String l) {
this.firstName = f;
this.lastName = l;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
}
//Account.java
package day10.Account_Customer_Bank;
/**
* @description: 账户类
* @author bit
* @date 2021/11/23 14:56
* @version 1.0
*/
public class Account {
private double balance;
public Account(double init_balance){
balance = init_balance;
}
public double getBalance() {
return balance;
}
/**
* @description: 存款
* @param amt 需要操作的钱数额
* @author bit
* @date 2021/11/23 14:43
*/
public void deposit(double amt){
if (amt > 0){
balance += amt;
System.out.println("成功存入:" + amt + ", 剩余余额为:" + balance);
}
}
/**
* @description: 取款
* @param amt 需要操作的钱数额
* @author bit
* @date 2021/11/23 14:46
*/
public void withdraw(double amt){
if(balance < amt){
System.out.println("余额不足!取款失败");
return;
}
balance -= amt;
System.out.println("成功取出!" + amt + ", 剩余余额为:" + balance);
}
}
//Bank.java
package day10.Account_Customer_Bank;
/**
* @description: 银行类
* @author bit
* @date 2021/11/23 14:57
* @version 1.0
*/
public class Bank {
private Customer[] customers; // 用于存放多个客户的数组
private int numberOfCustomer; // 记录客户的个数
public Bank() {
customers = new Customer[10];
}
/**
* @description: 添加一个客户
* @param f first name
* @param l last name
* @author bit
* @date 2021/11/23 14:57
*/
public void addCustomer(String f, String l){
Customer cust = new Customer(f, l);
// customer[numberOfCustomer] = cust;
// numberOfCustomer++;
customers[numberOfCustomer++] = cust;
}
public int getNumberOfCustomer() {
return numberOfCustomer;
}
public Customer getCustomer(int index) {
// return customers[index];//单纯这样写会出现越界等问题
if(index >= 0 && index < numberOfCustomer){
return customers[index];
}
return null;
}
}
//BankTest.java
package day10.Account_Customer_Bank;
public class BankTest {
public static void main(String[] args) {
Bank bank = new Bank();
// 创建新用户
bank.addCustomer("jane", "smith");
// 初始化smith用户的账户,设置初始余额为2000
bank.getCustomer(0).setAccount(new Account(2000));
// 得到在银行索引值为0的用户的用户对象,对此用户对象取款操作
bank.getCustomer(0).getAccount().withdraw(500);
double balance = bank.getCustomer(0).getAccount().getBalance();
System.out.println("客户" + bank.getCustomer(0).getFirstName() + "的账户余额为:" + balance);
System.out.println("--------------------");
bank.addCustomer("金", "白");
System.out.println("银行目前的客户数量为: " + bank.getNumberOfCustomer());
}
}
关键字:package的使用
- 为了更好的实现项目中类的管理,提供包的概念
- 按不同的功能分为不同的包,类就放在不同的包下面
- 使用package声明类或接口所属的包,声明在源文件的首行
- 包(package) 属于标识符,遵循标识符的命名规则、规范(xxx.yyy.zzz)、
见名知意
- 每
.
一次,就代表一层文件目录
- 补充:同一个包下,不能命名同名的接口、类;不同的包下,可以命名同名的接口、类
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) 的多个类, 这些类被用来构建和管理应用程序的图形用户界面(GUI)。 B/S C/S
MVC设计模式
MVC是常用的设计模式之一,将整个程序分为三个层次: 视图模型层(V),控制器层(C),与数据模型层(M)。 这种将程序输入输出、数据处理,以及数据的展示分离开来的设计模式使程序结构变的灵活而且清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性。
- 模型层 model 主要处理数据
- 数据对象封装 model.bean/domain
- 数据库操作类 model.dao
- 数据库 model.db
- 视图层 view 显示数据
- 相关工具类 view.utils
- 自定义view view.ui
- 视图层 view 显示数据
- 相关工具类 view.utils
- 自定义view view.ui
- 控制层 controller 处理业务逻辑
- 应用界面相关 controller.activity
- 存放fragment controller.fragment
- 显示列表的适配器 controller.adapter
- 服务相关的 controller.service
- 抽取的基类 controller.base
关键字:import的使用
import: 导入
- 在源文件中显式的使用import结构导入指定包下的类、接口
- 声明在包(package)的声明和类的声明之间
- 如果要导入多个结构,则并列写出即可
- 可以使用
xxx.*
的方式,表示可以导入xxx
包下所有的结构 - 如果使用的类或接口是
java.lang
包下定义的,则可以省略import结构 - 如果使用的类或接口是本包下定义的,则可以省略import结构
- 如果在源文件中,使用了不同包下的同名的类,则必须至少有一个类需要以全类名的方式显示
- 使用
xxx.*
方式可以调用xxx包下所有的结构,但如果使用的是xxx
子包下的结构,则仍需要显式导入 - import static:导入指定类或接口中的静态结构:属性或方法
package day10.java2;
import day10.Account_Customer_Bank.Account;
import day10.Account_Customer_Bank.Bank;
//导入包下的所有接口
import java.util.*;
// 使用java.util.*;即代表下面的结构
// import java.util.ArrayList;
// import java.util.Arrays;
// import java.util.HashMap;
import java.lang.reflect.Field;
import U4类与面向对象.day10.java2.java3.Dog;
//可以使用System类下接口静态的结构
import static java.lang.System.*;
import static java.lang.Math.*;
// 注意是类中的结构而不是类
// import static java.lang.Math;
public class PackageImportTest {
public static void main(String[] args) {
String info = Arrays.toString(new int[]{1,2,3});
Bank bank = new Bank();
ArrayList list = new ArrayList();
HashMap map = new HashMap();
Scanner s = null;
System.out.println("hello!");
Person p = new Person();
Account acct = new Account(1000);
//全类名的方式显示
day10.Account_Customer.Account acct1 = new day10.Account_Customer.Account(1000, 2000,0.0123);
//全类名的实际举例
Date date = new Date();
java.sql.Date date1 = new java.sql.Date(11L);
Dog dog = new Dog();
Field field = null;
out.println("hello");
long num = round(123.434);
}
}
考察
-
构造器的作用是什么?使用中有哪些注意点
-
作用
- 创建对象
- 初始化对象结构
-
注意点
- 一个类中一定有构造器,默认的构造器是无参的
- 构造器没有返回值
- 可以声明多个构造器
-
-
关于类的属性的赋值,有几种赋值方式。谈谈赋值的先后顺序。
- 默认初始化 -> 显示初始化 -> 构造器中初始化 ->
对象.方法
或对象.属性
赋值
- 默认初始化 -> 显示初始化 -> 构造器中初始化 ->
-
this关键字可以用来调用那些结构,简单说明一下其使用
- this: 属性、方法、构造器
- this 理解为当前对象,当前正在创建的对象
-
java中目前学习涉及到的四种权限修饰符都有什么?并说明各自的权限范围
- private、缺省、protected、public
-
创建Circle类,提供私有radius属性,提供相应的get和set方法,提供求圆面积的方法
private double radius; public double getRadius(){ return this.radius; } public void setRadius(double radius){ this.radius = radius; } public double findArea(){ return 3.14 * radius * radius; }
day11