故天将降大任于斯人也,必先苦其心志,劳其筋骨,饿其体肤**,**空乏其身,行拂乱其所为,所以动心忍性,曾益其所不能
面向对象内容的三条主线:
- Java 类及类的成员:属性、方法、构造器、代码块、内部类
- 面向对象的三大特征:封装、继承、多态性
- 其它关键字:this、super、static、final、abstract、interface、package、import
面向过程(POP)与面向对象(OOP)
- 二者都是一种思想,面向对象是相对于面向过程而言的。面向过程,强调的是功能行为,以函数为最小单位,考虑怎么做。面向对象,将功能封装进对 象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
- 面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如 抽象、分类、继承、聚合、多态等
一. 类和对象
类(Class)和对象(Object)是面向对象的核心概念
- 类是对一类事物的描述,是抽象的、概念上的定义
- 对象是实际存在的该类事物的每个个体,因而也称为实例(instance)
Java 类及类的成员
- 属 性:对应类中的成员变量
- 行 为:对应类中的成员方法
二. 类的成员:属性
2.1 语法格式
2.2 变量的分类
2.3 变量的区别
2.4 默认初始化值
当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。除了基本数据类型之外的变量类型都是引用类型
package com.base.learn;
/**
* @Author myf15609
* @Date 2022/12/10
*/
public class UserTest {
public static void main(String[] args) {
User user = new User();
// 默认初始化的情况
System.out.println(user.name); // null
System.out.println(user.age); // 0
System.out.println(user.isMail); // false
user.talk("汉语"); // 我们使用汉语交流
}
}
class User {
// 属性
String name;
int age;
boolean isMail;
public void talk(String language) {
System.out.println("我们使用" + language + "交流");
}
public void eat() {
// 局部变量
String food = "烙饼";
System.out.println("北方人喜欢吃 " + food);
}
}
三. 类的成员:方法
什么是方法(method、函数)
- 方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中 也称为函数或过程
- 将功能封装为方法的目的是,可以实现代码重用,简化代码
- Java里的方法不能独立存在,所有的方法必须定义在类里
3.1 方法的声明
3.2 方法的返回值
无返回值 | 有返回值 | |
---|---|---|
无形参 | void 方法名() {} | 返回值的类型 方法名() {} |
有行参 | void 方法名(行参列表) {} | 返回值的类型 方法名(行参列表) {} |
返回值类型: 有返回值 vs 没有返回值
- 如果方法有返回值,则必须在方法声明时,指定返回值的类型。同时,方法中,需要使用 return 关键字来返回指定类型的变量或常量:“return 数据”
- 如果方法没有返回值,则方法声明时,使用 void 来表示。通常,没有返回值的方法中,就不需要使用return.但是,如果使用的话,只能“return;”表示结束此方法的意思
3.3 匿名对象的使用
匿名对象,即没有名字的对象,指的是在创建一个对象时,只有创建语句,却没有将其地址赋值给某个变量
总结:
- 匿名对象作为对象,也具有普通对象的所有功能
- 每一次使用匿名对象,都是新 new 出来新对象
- 匿名对象执行完毕,由于没有其它引用,会被 Java 的垃圾回收机制自动进行回收
package com.base.learn;
public class InstanceTest {
public static void main(String[] args) {
Phone p = new Phone();
// 引用类型的变量,只可能是存储两类值:null / 地址值(含变量的类型)
System.out.println(p);
p.playGame();
p.sendEmail();
System.out.println("------分割线-------");
// 匿名
new Phone().sendEmail();
new Phone().playGame();
System.out.println("------分割线-------");
// 匿名对象的使用举例
PhoneMail phoneMail = new PhoneMail();
phoneMail.show(new Phone());
}
}
class PhoneMail {
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);
}
}
/*
com.base.learn.Phone@29453f44
打游戏
发送邮件
------分割线-------
发送邮件
打游戏
------分割线-------
发送邮件
打游戏
*/
3.4 方法的重载
Java 允许同一个类中定义多个同名方法,只要它们的形参列表不同即可。
如果同一个类中包含了两个或两个以上方法名相同的方法,但形参列表不同,这种情况被称为方法重载(overload)。
例如,在 JDK 的 java.io.PrintStream 中定义了十多个同名的 println() 方法
public void println(int i){…}
public void println(double d){…}
public void println(String s){…}
这些方法完成的功能类似,都是格式化输出。根据参数的不同来区分它们,以进行不同的格式化处理和输出。它们之间就构成了方法的重载。实际调用时,根据实参的类型来决定调用哪一个方法。例如:
System.out.println(102); // 调用println(int i)方法
System.out.println(102.25); // 调用println(double d)方法
System.out.println("价格为 102.25"); // 调用println(String s)方法
方法重载的要求是两同一不同:同一个类中方法名相同,参数列表不同。至于方法的其他部分,如方法返回值类型、修饰符等,与方法重载没有任何关系。
使用方法重载其实就是避免出现繁多的方法名,有些方法的功能是相似的,如果重新建立一个方法,重新取个方法名称,会降低程序可读性。
例子:
在比较数值时,数值的个数和类型是不固定的,可能是两个 int 类型的数值,也可能是两个 double 类型的数值,或者是两个 double、一个 int 类型的数值;在这种情况下就可以使用方法的重载来实现数值之间的比较功能。具体实现代码如下:
public class OverLoading {
public void max(int a, int b) {
// 含有两个int类型参数的方法
System.out.println(a > b ? a : b);
}
public void max(double a, double b) {
// 含有两个double类型参数的方法
System.out.println(a > b ? a : b);
}
public void max(double a, double b, int c) {
// 含有两个double类型参数和一个int类型参数的方法
double max = (double) (a > b ? a : b);
System.out.println(c > max ? c : max);
}
public static void main(String[] args) {
OverLoading ol = new OverLoading();
System.out.print("1 与 5 比较,较大的是:");
ol.max(1, 5);
System.out.print("5.205 与 5.8 比较,较大的是:");
ol.max(5.205, 5.8);
System.out.print("2.15、0.05、58 中,较大的是:");
ol.max(2.15, 0.05, 58);
}
}
/**
* 1 与 5 比较,较大的是:5
* 5.205 与 5.8 比较,较大的是:5.8
* 2.15、0.05、58 中,较大的是:58.0
*/
3.4 可变个数的行参
JavaSE 5.0 中提供了Varargs(variable number of arguments)机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参
JDK 5.0以前:采用数组形参来定义方法,传入多个同一类型变量 public static void test(int a ,String[] books);
JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量 public static void test(int a ,String…books);
- 举例:
public void show(int ... a){}
- 格式:数据类型 … 变量名
- 当调用可变个数行参的方法时,传入的参数的个数可以是:0个,1个,2个…
- 可变个数形参的方法与本类中方法名相同,形参不同的方法直接构成重载
- 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载,二者不可共存
- 假如要获取每一个填进去的元素(则把这种形式的变量当作一个数组就行)
- 方法的参数部分有可变行参,需要放在行参声明的末尾,最多只能声明一个可变行参
package com.base.learn;
public class MethodArgs {
public static void main(String[] args) {
MethodArgs methodArgs = new MethodArgs();
methodArgs.show(12);
methodArgs.show("hello");
methodArgs.show("hello", "world");
}
public void show(int i) {
System.out.println("show(int i)");
}
public void show(String s) {
System.out.println("show(String s)");
}
public void show(String... strs) {
System.out.println("show(String ... strs)");
for (String i : strs) {
System.out.println(i);
}
}
// 此方法于上一方法不可共存
// public void show(String[] strs){}
// 可变行参需要声明在末尾
public void show(int i, String... strs) {}
// 错误的举例
// public void show(String ... strs, int i) {}
}
/**
* show(int i)
* show(String s)
* show(String ... strs)
* hello
* world
*/
3.5 方法参数的值传递机制
package com.base.learn;
/**
* 关于变量的总结:
* 如果变量是基本数据类型,此时赋值的是变量所保存的数据值
* 如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值
*
*/
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);
n = 20;
System.out.println("m = " + m + ", n = " + n);
System.out.println("*********** 引用数据类型 ***********");
Order o1 = new Order();
o1.orderId = 10001;
// 赋值后,o1 和 o2 的地址值相同,都指向了堆空间中同一个对象实体
Order o2 = o1;
System.out.println("o1.orderId = " + o1.orderId + ",o2.orderId = " + o2.orderId);
o2.orderId = 10002;
System.out.println("o1.orderId = " + o1.orderId + ",o2.orderId = " + o2.orderId);
}
}
class Order {
int orderId;
}
/*
*********** 基本数据类型 ***********
m = 10, n = 10
m = 10, n = 20
*********** 引用数据类型 ***********
o1.orderId = 10001,o2.orderId = 10001
o1.orderId = 10002,o2.orderId = 10002
*/
3.5.1 基本数据类型
package com.base.learn;
/**
* 方法的行参的值传递机制:值传递
*
* 1. 行参:方法定义时,声明的小括号内的参数
* 实参:方法调用时,实际传递给行参的数据
*
* 2. 值传递机制:
* 如果参数是基本数据类型,此时实参赋值给行参的是实参真实存储的数据值
*
*/
public class ValueTransferTest1 {
public static void main(String[] args) {
int m = 10;
int n = 20;
System.out.println("m = " + m + ", n = " + n); // m = 10, n = 20
// 交换两个变量的值
int temp = m;
m = n;
n = temp;
System.out.println("m = " + m + ", n = " + n); // m = 20, n = 10
ValueTransferTest1 test1= new ValueTransferTest1();
test1.swap(m, n);
System.out.println("m = " + m + ", n = " + n); // m = 20, n = 10
}
public void swap(int m, int n) {
int temp = m;
m = n;
n = temp;
}
}
3.5.2 引用数据类型
package com.base.learn;
/**
* 如果参数是引用数据类型,此时实参赋值给行参的是实参存储数据的地址值
*
*/
public class ValueTransferTest2 {
public static void main(String[] args) {
Data data = new Data();
data.m = 10;
data.n = 20;
System.out.println("m = " + data.m + ", n = " + data.n); // m = 10, n = 20
ValueTransferTest2 test2 = new ValueTransferTest2();
test2.swap(data);
System.out.println("m = " + data.m + ", n = " + data.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;
}
3.5.3 练习1
package com.base.learn;
public class ValueTransferTest3 {
public static void main(String[] args) {
ValueTransferTest3 test3 = new ValueTransferTest3();
test3.first();
}
public void first() {
int i = 5;
Value v = new Value();
v.i = 25;
second(v, i);
System.out.println(v.i); // 20
}
public void second(Value v, int i) {
i = 0;
v.i = 20;
Value val = new Value();
v = val;
System.out.println(v.i + " " + i); // 15, 0
}
}
class Value {
int i = 15;
}
3.5.4 练习2
package com.base.learn;
/**
* 使用 System.exit(0); 终止当前 jvm 的执行
*
*/
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 10;
method(a, b); // 需要在 method 方法被调用之后,仅打印出 a=100, b=200 ,请写出 method 方法的代码
System.out.println("a = " + a);
System.out.println("b = " + b);
}
public static void method(int a, int b) {
a = a * 10;
b = b * 10;
System.out.println("a = " + a);
System.out.println("b = " + b);
System.exit(0);
}
}
3.5.5 练习3
package com.base.learn;
import java.util.Arrays;
/**
* 微软:
* 定义一个int型的数组:int[] arr = new int[]{12,3,3,34,56,77,432};
* 让数组的每个位置上的值去除以首位置的元素,得到的结果,作为该位置上的新值。遍历新的数组。
*/
public class Test3 {
public static void main(String[] args) {
int[] arr = new int[]{12, 3, 3, 34, 56, 77, 432};
// 错误的写法:第一次除完 arr[0] = 1 了
for (int i = 0; i < arr.length; i++) {
arr[i] = arr[i] / arr[0];
}
System.out.println(Arrays.toString(arr)); // [1, 3, 3, 34, 56, 77, 432]
// 正确的写法1:倒着来
int[] arr2 = new int[]{12, 3, 3, 34, 56, 77, 432};
for (int i = arr2.length - 1; i >= 0; i--) {
arr2[i] = arr2[i] / arr2[0];
}
System.out.println(Arrays.toString(arr2)); // [1, 0, 0, 2, 4, 6, 36]
// 正确的写法2:用临时变量存储 arr3[0]
int[] arr3 = new int[]{12, 3, 3, 34, 56, 77, 432};
int temp = arr3[0];
for (int i = 0; i < arr.length; i++) {
arr3[i] = arr3[i] / temp;
}
System.out.println(Arrays.toString(arr3)); // [1, 0, 0, 2, 4, 6, 36]
}
}
3.5.6 练习4
package com.base.learn;
public class Test4 {
public static void main(String[] args) {
int[] arr1 = new int[]{1, 2, 3};
System.out.println(arr1); // [I@29453f44
char[] arr2 = new char[]{'a', 'b', 'c'};
System.out.println(arr2); // abc
}
}
public void println(char x[]) {
synchronized (this) {
print(x);
newLine();
}
}
3.5.7 练习5
package com.base.exer;
/**
* (1)定义一个Circle类,包含一个double型的radius属性代表圆的半径,一个findArea()方法返回圆的面积。
*/
public class Circle {
double radius; // 半径
public double findArea() {
return Math.PI * radius * radius;
}
}
package com.base.exer;
/**
* (2) 定义一个类PassObject,在类中定义一个方法printAreas(),该方法的定义如下:
* public void printAreas(Circle c,int time)
* 在printAreas方法中打印输出1到time之间的每个整数半径值,以及对应的面积。
* 例如,times为5,则输出半径1,2,3,4,5,以及对应的圆面积。
*
* (3) 在main方法中调用printAreas()方法,调用完毕后输出当前半径值
*/
public class PassObject {
public static void main(String[] args) {
PassObject test = new PassObject();
Circle c = new Circle();
test.printAreas(c, 5);
System.out.println("no radius is:" + c.radius);
}
public void printAreas(Circle c, int time) {
System.out.println("Radius\t\tAreas");
// 设置圆的半径
for (int i = 1; i <= time; i++) {
c.radius = i;
System.out.println(c.radius + "\t\t" + c.findArea());
}
}
}
/*
Radius Areas
1.0 3.141592653589793
2.0 12.566370614359172
3.0 28.274333882308138
4.0 50.26548245743669
5.0 78.53981633974483
no radius is:5.0
*/
3.6 递归调用
package com.base.exer1;
/**
* 递归方法的使用(了解)
* 1.递归方法:一个方法体内调用它自身。
* 2.方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
*
* 3.递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
*/
public class RecursionTest {
public static void main(String[] args) {
// 例1:计算 1-100 之间所有自然数的和
// 方法1:for 循环
int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
}
System.out.println("sum = " + sum);
// 方法2:递归调用
RecursionTest test = new RecursionTest();
int sum2 = test.getSum(100);
System.out.println("sum2 = " + sum2);
}
public int getSum(int n) {
if (n == 1) {
return 1;
} else {
return n + getSum(n - 1);
}
}
}
// sum = 5050
// sum2 = 5050
3.6.1 练习1
package com.base.exer1;
/**
* 已知有一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),其中n是大于0 的整数,求f(10)的值。
*/
public class RecursionTest1 {
public static void main(String[] args) {
RecursionTest1 test1 = new RecursionTest1();
int a = test1.f(10);
System.out.println(a); // 10497
}
public int f(int n) {
if (n == 0) {
return 1;
} else if (n == 1) {
return 4;
} else {
return 2 * f(n - 1) + f(n - 2);
}
}
}
3.6.2 练习2
package com.base.exer1;
/**
* 斐波那契数列(Fibonacci)
* 问:什么是斐波那契数列?
* 答:一个数等于前两个数之和(例如:1 1 2 3 5 8 13 21 34 55)
* 问题:计算斐波那契数列的第n个值
*/
public class RecursionTest2 {
public static void main(String[] args) {
RecursionTest2 test2 = new RecursionTest2();
int a = test2.f(10);
System.out.println(a); // 55
}
public int f(int n) {
if (n == 1 || n == 2) {
return 1;
} else {
return f(n - 1) + f(n - 2);
}
}
}
3.7 访问控制修饰符
访问级别 | 访问控制修饰符 | 同类 | 同包 | 子类 | 不同包 |
---|---|---|---|---|---|
公开 | public | ✓ | ✓ | ✓ | ✓ |
受保护 | protected | ✓ | ✓ | ✓ | ✕ |
默认 | 无 | ✓ | ✓ | ✕ | ✕ |
私有 | private | ✓ | ✕ | ✕ | ✕ |
3.8 构造方法
3.8.1 构造方法总结
构造方法是类的一种特殊方法,用来初始化类的一个新的对象,在创建对象(new 运算符)之后自动调用。Java 中的每个类都有一个默认的构造方法,并且可以有一个以上的构造方法。
Java 构造方法有以下特点:
- 方法名必须与类名相同
- 可以有 0 个、1 个或多个参数
- 没有任何返回值,包括 void
- 默认返回类型就是对象类型本身
- 只能与 new 运算符结合使用
一旦显示的定义了类的构造器之后,系统不再提供默认的空参构造器。
值得注意的是,如果为构造方法定义了返回值类型或使用 void 声明构造方法没有返回值,编译时不会出错,但 Java 会把这个所谓的构造方法当成普通方法来处理。
这时候大家可能会产生疑问,构造方法不是没有返回值吗?为什么不能用 void 声明呢?
简单的说,这是 Java 的语法规定。实际上,类的构造方法是有返回值的,当使用 new 关键字来调用构造方法时,构造方法返回该类的实例,可以把这个类的实例当成构造器的返回值,因此构造器的返回值类型总是当前类,无须定义返回值类型。但必须注意不要在构造方法里使用 return 来返回当前类的对象,因为构造方法的返回值是隐式的。
注意:构造方法不能被 static、final、synchronized、abstract 和 native(类似于 abstract)修饰。构造方法用于初始化一个新对象,所以用 static 修饰没有意义。构造方法不能被子类继承,所以用 final 和 abstract 修饰没有意义。多个线程不会同时创建内存地址相同的同一个对象,所以用 synchronized 修饰没有必要。
在一个类中定义多个具有不同参数的同名方法,这就是方法的重载
public class WorkerTest {
public static void main(String[] args) {
System.out.println("---------带有一个参数的构造方法---------");
Worker worker1 = new Worker("张三");
System.out.println("大家好!我是新来的员工,我叫 "+ worker1.name + ",今年 " + worker1.age + " 岁。");
System.out.println("---------带有一个参数的构造方法---------");
Worker worker2 = new Worker("李四", 18);
System.out.println("大家好!我是新来的员工,我叫 "+ worker2.name + ",今年 " + worker2.age + " 岁。");
}
}
class Worker{
public String name;
public int age;
// 定义带有一个参数的构造方法
public Worker(String name) {
this.name = name;
}
// 定义带有两个参数的构造方法
public Worker(String name, int age) {
this.name = name;
this.age = age;
}
}
/*
---------带有一个参数的构造方法---------
大家好!我是新来的员工,我叫 张三,今年 0 岁。
---------带有一个参数的构造方法---------
大家好!我是新来的员工,我叫 李四,今年 18 岁。
*/
3.8.2 总结属性赋值过程
package com.base.learn;
/**
* 总结:属性赋值的先后顺序
* <p>
* ① 默认初始化值
* ② 显式初始化
* ③ 构造器中赋值
* ④ 通过"对象.方法" 或 “对象.属性”的方式,赋值
* <p>
* 以上操作的先后顺序:① - ② - ③ - ④
*/
public class UserTest {
public static void main(String[] args) {
User u = new User();
System.out.println(u.age); // 1
User u2 = new User(2);
System.out.println(u2.age); // 2
u2.setAge(3);
System.out.println(u2.age); // 3
}
}
class User {
String name;
int age = 1;
public User() {
}
public User(int age) {
this.age = age;
}
public void setAge(int age) {
this.age = age;
}
}
3.8.3 JavaBean
JavaBeans是Java中一种特殊的类,可以将多个对象封装到一个对象(bean)中。特点是可序列化,提供无参构造器,提供getter方法和setter方法访问对象的属性。名称中的“Bean”是用于Java的可重用软件组件的惯用叫法。
- JavaBean是一种Java类,而且是一种特殊的、可重用的类。
- JavaBean必须具有无参数的构造器,所有的属性都是private的,通过提供setter和getter方法来实现对成员属性的访问。
- Javabean 是为了和 jsp 页面传数据化简交互过程而产生的。
package com.base.learn;
/**
* 客户信息类
*
*/
public class Customer {
private String name; // 名字
private char gender; // 性别
private int age; // 年龄
private String phone; // 手机号
private String email; // 电子邮箱
public Customer() {
}
public Customer(String name, char gender, int age, String phone, String email) {
this.name = name;
this.gender = gender;
this.age = age;
this.phone = phone;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
3.8.4 UML 类图
详细:https://www.zhihu.com/question/419312525
3.9 this 关键字
3.9.1 this 调用属性、方法、构造器
package com.base.exer;
/**
* this 关键字的使用
* 1.this 用来修饰、调用:属性、方法、构造器
* <p>
* 2.this 修饰属性和方法:
* this 理解为:当前对象,或当前正在创建的对象。
* <p>
* 2.1 在类的方法中,我们可以使用"this.属性"或"this.方法"的方式,调用当前对象属性和方法。
* 通常情况下,我们都选择省略“this.”。特殊情况下,如果方法的形参和类的属性同名,我们必须显式
* 的使用"this.变量"的方式,表明此变量是属性,而非形参。
* <p>
* 2.2 在类的构造器中,我们可以使用"this.属性"或"this.方法"的方式,调用正在创建的对象属性和方法。
* 但是,通常情况下,我们都选择省略“this.”。特殊情况下,如果构造器的形参和类的属性同名,我们必须显式
* 的使用"this.变量"的方式,表明此变量是属性,而非形参。
* <p>
* 3.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("jerry", 20);
System.out.println(p2.getAge());
}
}
class Person {
private String name;
private int age;
public Person() {
this.eat();
String info = "Person info.";
System.out.println(info);
}
public Person(String name) {
this();
this.name = name;
}
public Person(int age) {
this.age = age;
}
public Person(String name, int age) {
this(age);
this.name = name;
}
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 eat() {
System.out.println("人吃饭");
}
}
3.9.2 练习1 关于 this 的使用
package com.base.learn;
public class Boy {
private String name;
private int age;
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("好好学习");
}
}
}
package com.base.learn;
public class Girl {
private String name;
private int age;
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 int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void marry(Boy boy) {
System.out.println("我想嫁给" + boy.getName());
// this 是"当前对象",这了谁调用了 marry 谁就是 this
boy.marry(this);
}
public int compare(Girl girl) {
return this.age - girl.age;
}
}
package com.base.learn;
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("年龄一样大");
}
}
}
/*
好好学习
我想嫁给罗密欧
我想娶朱丽叶
祝英台年龄大
*/
3.9.3 练习2 取钱存钱
写一个名为 Account 的类模拟账户。该类的属性和方法如下图所示。该类包括的属性: 账号 id,余额 balance,年利率 annualInterestRate
包含的方法:访问器方法(getter 和 setter 方法),取款方法 withdraw(),存款方法 deposit()
在提款方法 withdraw 中,需要判断用户余额是否能够满足提款数额的要求,如果不能,应给出提示
package com.base.exer;
public class Account {
private int id; // 账号
private double balance; // 余额
private double annualInterestRate; // 年利率
public Account(int id, double balance, double annualInterestRate) {
this.id = id;
this.balance = balance;
this.annualInterestRate = annualInterestRate;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public double getAnnualInterestRate() {
return annualInterestRate;
}
public void setAnnualInterestRate(double annualInterestRate) {
this.annualInterestRate = annualInterestRate;
}
/**
* 取钱操作
*
* @param amount 金额
*/
public void withdraw(double amount) {
if (balance < amount) {
System.out.println("余额不足,取款失败!");
return;
}
balance -= amount;
System.out.println("成功取出:" + amount);
}
/**
* 存钱操作
*
* @param amount 金额
*/
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("成功存入:" + amount);
}
}
}
- 声明三个私有对象属性:firstName、lastName 和 account。
- 声明一个公有构造器,这个构造器带有两个代表对象属性的参数(f 和 l)
- 声明两个公有存取器来访问该对象属性,方法 getFirstName 和 getLastName 返回相应的属性。
- 声明 setAccount 方法来对 account 属性赋值。
- 声明 getAccount 方法以获取 account 属性
package com.base.exer;
public class Customer {
private String firstName;
private String lastName;
private Account account;
public Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
}
package com.base.exer;
/*
1. 创建一个 Customer ,名字叫 Jane Smith, 他有一个账号为 1000,余额为 2000 元,年利率为 1.23% 的账户。
2. 对 Jane Smith 操作。存入 100 元,再取出 960 元。再取出 2000 元。打印出 Jane Smith 的基本信息
*/
public class CustomerTest {
public static void main(String[] args) {
Customer cust = new Customer("Jane", "Smith");
Account acct = new Account(1000, 2000, 0.0123);
cust.setAccount(acct);
cust.getAccount().deposit(100);
cust.getAccount().withdraw(960);
cust.getAccount().withdraw(2000);
System.out.println("Custormer [" + cust.getLastName() + ", " + cust.getFirstName() + "] has a account: id is "
+ cust.getAccount().getId() + ", annualInterestRate is "
+ cust.getAccount().getAnnualInterestRate() * 100 + "%, balance is "
+ cust.getAccount().getBalance());
}
}
/*
成功存入:100.0
成功取出:960.0
余额不足,取款失败!
Custormer [Smith, Jane] has a account: id is 1000, annualInterestRate is 1.23%, balance is 1140.0
*/
3.9.3 练习3 银行账户
在提款方法 withdraw() 中,需要判断用户余额是否能满足提款数额的要求,如果不能应给出提示。
deposit() 方法表示存款
package com.base.exer1;
/**
* 账户类 Account
*
*/
public class Account {
private double balance;
public Account(double balance) {
this.balance = balance;
}
public double getBalance() {
return balance;
}
/**
* 存钱操作
*
* @param amount 金额
*/
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("成功存入:" + amount);
}
}
/**
* 取钱操作
*
* @param amount 金额
*/
public void withdraw(double amount) {
if (balance < amount) {
System.out.println("余额不足,取款失败!");
return;
}
balance -= amount;
System.out.println("成功取出:" + amount);
}
}
package com.base.exer1;
/**
* 用户类 Customer
*
*/
public class Customer {
private String firstName;
private String lastName;
private Account account;
public Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
}
package com.base.exer1;
/**
* 银行类 Bank
*
*/
public class Bank {
private Customer[] customers; // 存放多个客户的数组
private int numberOfCustomers; // 记录客户的个数
public Bank() {
customers = new Customer[10];
}
/**
* 添加客户操作
*
* @param f firstName
* @param l lastName
*/
public void addCustomer(String f, String l) {
Customer cust = new Customer(f, l);
customers[numberOfCustomers++] = cust;
}
/**
* 获取指定位置上的客户
*
* @return customers[index]
*/
public Customer getCustomers(int index) {
if (index >= 0 && index < numberOfCustomers) {
return customers[index];
} else {
return null;
}
}
/**
* 获取客户个数
*
* @return numberOfCustomers
*/
public int getNumberOfCustomers() {
return numberOfCustomers;
}
}
package com.base.exer1;
/**
* 银行测试类 BankTest
*
*/
public class BankTest {
public static void main(String[] args) {
Bank bank = new Bank();
bank.addCustomer("Jane", "Smith");
bank.getCustomers(0).setAccount(new Account(2000));
bank.getCustomers(0).getAccount().withdraw(500);
bank.getCustomers(0).getAccount().deposit(100);
double balance = bank.getCustomers(0).getAccount().getBalance();
System.out.println("客户:" + bank.getCustomers(0).getFirstName() + " 的账户余额为:" + balance);
System.out.println("-------------------");
bank.addCustomer("三", "张");
System.out.println("银行客户的个数为:" + bank.getNumberOfCustomers());
}
}
/*
成功取出:500.0
成功存入:100.0
客户:Jane 的账户余额为:1600.0
-------------------
银行客户的个数为:2
*/
四. package、import
4.1 关键字 package
基础理解:
- package 语句作为 Java 源文件的第一条语句,指明该文件中定义的类所在的包。
- 它的格式为: package 顶层包名.子包名
- 为了更好的实现项目中类的管理,提供包的概念
- 使用 package 声明类或接口所属的包,声明在原文件的首行
- 包,属于标识符,遵循标识符的命名规则和规范,“见名知意”,所有字母都小写
- 每“.”一次代表一层文件目录
- 补充:同一个包下不能命名同名的接口、类,不同的包下可以命名同名接口或者类
JDK中主要的包介绍
- java.lang----包含一些Java语言的核心类,如String、Math、Integer、 System和 Thread,提供常用功能
- java.net----包含执行与网络相关的操作的类和接口
- java.io ----包含能提供多种输入/输出功能的类
- java.util----包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日 期日历相关的函数
- java.text----包含了一些java格式化相关的类
- java.sql----包含了java进行JDBC数据库编程的相关类/接口
4.2 关键字 import
- 为使用定义在不同包中的Java类,需用import语句来引入指定包层次下所需要的类 或全部类(.*)。import语句告诉编译器到哪里去寻找类。
- import 包名. 类名;
- 在源文件中显式的使用 import 结构导入指定包下的类、接口
- 声明在包的声明和类的声明之间
- 如果需要导入多个结构,则并列写出即可
- 可以使用 “xxx.*” 的方式,表示可以导入 xxx 包下的所有结构。
- 如果导入的类或接口是 java.lang 包下的,或者是当前包下的,则可以省略此 import 语句。
- 如果在代码中使用不同包下的同名的类。那么就需要使用类的全类名的方式指明调用的是哪个类。
- 如果已经导入 java.a 包下的类。那么如果需要使用 a 包的子包下的类的话,仍然需要导入。
- import static 组合的使用:调用指定类或接口下的静态的属性或方法.
4.3 MVC 设计
MVC 是常用的设计模式之一,将整个程序分为三个层次:视图模型层,控制器层,数据模型层。这种将程序输入输出、数据处理,以及数据的展示分离开来的设计模式使程序结构变的灵活而且清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性。