六、 面向对象(上)
6.0 面向对象的三大思想
- 面向对象思想编程内容的三条主线:
① 类及类的成员:属性、方法、构造器;代码块、内部类
② 面向对象的三大特征:封装性、继承性、多态性
③ 其它关键字:this,super,abstract,interface,static,final,package,import
6.1 类的成员一:属性
6.1.2 属性与局部变量
不同点:
( 1 \mathbf{1} 1) 属性/成员变量可以在声明前添加权限修饰符。如
public/private
( 2 \mathbf{2} 2) 属性/成员变量在类的大括号中就已经声明,局部变量只在类的方法中声明。
属性/成员变量 | 局部变量 |
---|---|
默认初始化值和数组情况一样 | 没有默认初始化值,必须显式赋值。但形参可以等到调用方法再赋值。 |
在内存的堆中 | 在内存的栈中 |
6.2 类的成员二:方法
权限修饰符 返回值类型 方法名 (形参列表){
方法体
}
( 1 \mathbf{1} 1) 方法里面可以再调方法。但不能在方法里定义方法。
6.2.1 对象数组题目
/**
* 4. 对象数组题目:
* 定义类Student,包含三个属性:学号number(int),
年级state(int),成绩score(int)。
* 创建20个学生对象,学号为1到20,年级和成绩都由随机数确定。
* 问题一:打印出3年级(state值为3)的学生信息。
* 问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息。
*
* 提示:
* 1) 生成随机数:Math.random(),返回值类型double;
* 2) 四舍五入取整:Math.round(double d),返回值类型long。
*/
我首次代码:
public class Exercise4StudentTest {
public static void main(String[] args) {
//声明一个Student类型的数组
Student[] stuArr = new Student[20];
/**
* 注意!此时堆中20个数组都是null。必须逐一new Student对象才能正常使用
*/
for (int i=0; i<stuArr.length; i++){
stuArr[i] = new Student(); //这一步我第一次做时漏了!
stuArr[i].number = (i+1);
stuArr[i].state = (int) (Math.random() * 10) + 1;
stuArr[i].score = (int) (Math.random() * 100);
}
//问题一:打印出3年级(state值为3)的学生信息。
for (int i=0; i<stuArr.length; i++){
if (stuArr[i].state == 3){
stuArr[i].say();
System.out.println("-----------------------");
}
}
//问题二:使用冒泡排序按学生成绩排序,并遍历所有学生信息。
for (int i = stuArr.length-1; i>=0; i--){
for (int j=0; j<i; j++){
if (stuArr[j].score > stuArr[j+1].score){
Student temp = new Student();
temp = stuArr[j];
stuArr[j] = stuArr[j+1];
stuArr[j+1] = temp;
}
}
}
//遍历所有学生信息.
for (int i=0; i<stuArr.length; i++){
stuArr[i].say();
System.out.println("----------------------");
}
}
}
class Student{
int number;
int state;
int score;
public void say(){
System.out.println("学号:" + number);
System.out.println("年级:" + state);
System.out.println("成绩:" + score);
}
}
代码优化思路:
( 1 \mathbf{1} 1) 把两个需求分别封装到方法里,main方法里直接调用方法可以使代码看起来更加简洁美观。
优化后的代码:
public class Exercise4StudentTest {
public static void main(String[] args) {
//声明一个Student类型的数组
Student[] stuArr = new Student[20];
/**
* 注意!此时堆中20个数组都是null。必须逐一new Student对象才能正常使用
*/
for (int i=0; i<stuArr.length; i++){
stuArr[i] = new Student(); //这一步我第一次做时漏了!
stuArr[i].number = (i+1);
stuArr[i].state = (int) (Math.random() * 6) + 1;
stuArr[i].score = (int) (Math.random() * 100) + 1;
}
//创建测试对象
Exercise4StudentTest test = new Exercise4StudentTest();
//1. 遍历对象数组的属性,并打印出来
test.print(stuArr);
//2. 打印出k年级(state值为k)的学生信息
test.search(stuArr, 5);
//3.使用冒泡排序按学生成绩排序
test.sort(stuArr);
//遍历对象数组的属性,并打印出来
test.print(stuArr);
}
/**
* 遍历对象数组的属性,并打印出来
* @param stuArr: 需要打印的对象数组
*/
public void print(Student[] stuArr){
for (int i=0; i<stuArr.length; i++){
stuArr[i].say();
}
}
/**
*打印出k年级(state值为k)的学生信息
* @param stuArr: 需要搜索的对象数组
* @param k: 需要查找的学号
*/
public void search (Student[] stuArr, int k){
for (int i=0; i<stuArr.length; i++){
if (stuArr[i].state == k){
stuArr[i].say();
System.out.println("-----------------------");
}
}
}
/**
* 使用冒泡排序按学生成绩排序
* @param stuArr:需要排序的数组
*/
public void sort (Student[] stuArr){
for (int i = stuArr.length-1; i>=0; i--){
for (int j=0; j<i; j++){
if (stuArr[j].score > stuArr[j+1].score){
Student temp = new Student();
temp = stuArr[j];
stuArr[j] = stuArr[j+1];
stuArr[j+1] = temp;
}
}
}
}
}
class Student{
int number;
int state;
int score;
public void say(){
System.out.println("学号:" + number + " 年级:" + state + " 成绩:" + score);
}
}
6.3 匿名对象
6.3.1 匿名对象的声明
( 1 \mathbf{1} 1) 构造一个Phone类:
class Phone{
double price;//价格
public void showPrice(){
System.out.println("手机价格为:" + price);
}
public void sendEmail(){
System.out.println("发生邮件");
}
public void playGames(){
System.out.println("玩游戏");
}
}
( 2 \mathbf{2} 2) 创建一个Phone类对象,并分别写非匿名对象和匿名对象:
public class AnonymousObject {
public static void main(String[] args) {
//非匿名对象
Phone p = new Phone();
p.sendEmail();
p.playGames();
//匿名对象
new Phone().sendEmail();
new Phone().playGames();
}
}
输出结果:
发生邮件
玩游戏
发生邮件
玩游戏
6.3.2 匿名对象特点
( 1 \mathbf{1} 1) 只能调用一次,重复调用不相同。见下面例子:
new Phone().price = 2999;
new Phone().showPrice();
输出结果:
手机价格为:0.0
6.3.3 开发中的使用方法
( 1 \mathbf{1} 1) 在上述代码的基础上,新建一个类:
class PhoneMall{
public void show(Phone phone){
phone.sendEmail();
phone.playGames();
}
}
其中,方法show的形参是Phone类型的对象,方法体中又调用了Phone类中的方法。
( 2 \mathbf{2} 2) 在main方法中新建PhoneMall类的对象,并调用类中的show方法:
PhoneMall mall = new PhoneMall();
mall.show(new Phone()); //匿名对象的形参
输出结果:
发送邮件
玩游戏
( 3 \mathbf{3} 3) 由于show方法的形参必须是Phone类型的对象,所以可以使用匿名对象新建一个Phone对象。
6.4 方法的重载
6.4.1 定义
在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
“两同一不同”:同一个类,同一个方法名;参数列表不同。
个人理解:
( 1 \mathbf{1} 1) 之所以要允许同名方法的存在,是因为同一个方法,形参的数据类型不同,比如int、char、String、double等,没有必要因为形参的数据类型不同而起不同的方法名。
( 2 \mathbf{2} 2) 判断方法是否重载跟权限修饰符、返回值类型、形参变量名、方法体都没有关系。只跟方法名和形参列表是否相同有关。
( 3 \mathbf{3} 3) 在通过对象调用方法时,除了看方法名,还要看形参列表来确定某一个指定的方法。
6.4.2 举例
//如下的4个方法构成了重载
public void getSum(int i, int j){
System.out.println(i + j);
}
public void getSum(double d1, double d2){
System.out.println(d1 + d2);
}
public void getSum(String i, int j){
System.out.println(i + j);
}
public void getSum(int j, String i){
System.out.println(i + j);
}
第3个与第4个因为形参顺序不同,也算方法重载。
//如下的3个方法不能与上述4个方法构成重载
// public int getSum(int i,int j){
// return 0;
// }
// public void getSum(int m,int n){
//
// }
// private void getSum(int i,int j){
//
// }
6.4.3 练习题
(
1
\mathbf{1}
1) 编写程序,定义三个重载方法并调用。方法名为mOL。
三个方法分别接收一个int参数、两个int参数、一个字符串参数。
分别执行平方运算并输出结果,相乘并输出结果,输出字符串信息。在主类的main ()方法中分别用参数区别调用三个方法。
我的首次答案:
public class OverLoad {
public static void main(String[] args) {
OverLoad test = new OverLoad();
test.mOL(5);
test.mOL(5, 6);
test.mOL("Deep Dark Fantasies");
}
public void mOL(int i){
System.out.println(i*i);
}
public void mOL(int i, int j){
System.out.println(i*j);
}
public void mOL(String i){
System.out.println(i);
}
}
输出结果:
25
30
Deep Dark Fantasies
(
2
\mathbf{2}
2) 定义三个重载方法max(),
第一个方法求两个int值中的最大值,
第二个方法求两个double值中的最大值,
第三个方法求三个double值中的最大值,
并分别调用三个方法。
我的首次答案:
public class OverLoad {
public static void main(String[] args) {
OverLoad test = new OverLoad();
//2.三个重载方法max()调用
System.out.println(test.max(3, 93));
System.out.println(test.max(3.3, 55.6));
System.out.println(test.max(99.9, 3.3, 55.5));
}
//2.三个重载方法max()
public int max(int i, int j){
if (i>j){
return i;
}else {
return j;
}
}
public double max(double d1, double d2){
if (d1>d2){
return d1;
}else {
return d2;
}
}
public double max(double d1, double d2, double d3){
double max = d1;
if (d2>max) {
max = d2;
if (d3>max){
max = d3;
}
}else if (d3>max){
max = d3;
}
return max;
}
}
输出结果:
93
55.6
99.9
优化思路:
( 1 \mathbf{1} 1) 对方法max() 进行优化:
public int max(int i, int j){
return (i>j)? i : j;
}
public double max(double d1, double d2){
return (d1>d2)? d1 : d2;
}
public double max(double d1, double d2, double d3){
double max = (d1>d2)? d1 : d2;
return (max>d3)? max : d3;
}
6.5 可变形参的方法
6.5.1 可变个数形参的声明格式
Varargs (variable number of arguments) 机制:参数数量可变。
public void show(数据类型 ... 参数名){
方法体;
}
6.5.2 调用
public class VariableArguments {
public static void main(String[] args) {
VariableArguments test = new VariableArguments();
test.show();
test.show("Boy");
test.show("Boy", "Next Door");
test.show("Deep", "Dark", "Fantasies");
}
public void show(String ... strings){
System.out.println("show(String ... strings)");
}
}
输出结果:
show(String ... strings)
show(String ... strings)
show(String ... strings)
show(String ... strings)
( 1 \mathbf{1} 1) 可以看到,传入的形参个数可以为0个、1个、2个、3个…
( 2 \mathbf{2} 2) 注意。当有确定形参个数的方法和可变个数形参方法同时存在时,程序会优先选择前者。(程序的确定性)
( 3 \mathbf{3} 3) 可变个数形参方法可以构成重载。
( 4 \mathbf{4} 4) 下列不构成重载:
public void show(String ... strings){
System.out.println("show(String ... strings)");
}
public void show(String[] strings){
System.out.println("show(String ... strings)");
}
它们的形参列表被认为是相同的。
( 5 \mathbf{5} 5) 遍历也是和数组一样:
public class VariableArguments {
public static void main(String[] args) {
VariableArguments test = new VariableArguments();
test.show();
test.show("Boy");
test.show("Boy", "Next Door");
test.show("Deep", "Dark", "Fantasies");
}
public void show(String ... strings){
System.out.println("-------------------------");
for (int i=0; i<strings.length; i++){
System.out.println(strings[i]);
}
}
}
输出结果:
-------------------------
-------------------------
Boy
-------------------------
Boy
Next Door
-------------------------
Deep
Dark
Fantasies
( 6 \mathbf{6} 6) 可变个数形参,必须声明在末尾。如:
public void show(int i, String ... strings){
}
是可行的。但如果上述两个形参对调位置,则不行!
( 7 \mathbf{7} 7) 由上述可推断,可变个数形参最多只能声明一个。
6.5.3 应用场景
在SQL数据库的时候,不知道用户后面要写多少个String型,就可以用可变个数形参。
6.6 方法参数的值传递机制 (重点)
6.6.1 形参和实参
( 1 \mathbf{1} 1) 形参:方法声明时的参数。
( 2 \mathbf{2} 2) 实参:方法调用时实际传给形参的参数值。
6.6.2 实参传入方法的方式
Java里方法的参数传递方式只有一种: 值传递 。 即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响 。
值传递机制:
( 1 \mathbf{1} 1) 形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参。
( 2 \mathbf{2} 2) 形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参。
6.6.3 值传递举例说明
( 1 \mathbf{1} 1) 形参是基本数据类型:
问题:交换m和n中的值。
//两数交换方法
public static void trans(int a, int b) {
int temp = a;
a = b;
b = temp;
System.out.println("a=" + a);
System.out.println("b=" + b);
}
public static void main(String[] args) {
int a = 1;
int b = 2;
trans(a, b);
System.out.println("---------------");
System.out.println("a=" + a + ", b=" + b);//原始的变量不会被改变
}
输出:
a=2
b=1
---------------
a=1, b=2
可以看到,对于基本数据类型来说,虽然 trans(int a, int b)
方法内,形参 a
和形参 b
被交换了值,但是方法外的变量 a
和 b
的值没有因此受到影响,保持原来的值不变。
这就是 Java 中的基本数据类型的值传递机制,只会传递参数的“数据值”,而不影响真正的变量。
( 2 \mathbf{2} 2) 形参是引用数据类型:
//形参是引用数据类型的值传递,传递的是地址值
public static void myArray(int[] arr) {
arr[0] = 100;
for (int i : arr) {
System.out.println(i);
}
}
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4};
myArray(arr);
System.out.println("-----------");
for (int i : arr) {
System.out.println(i);//直接传递地址,原始数组会改变
}
}
输出:
100
2
3
4
-----------
100
2
3
4
可以看到,对于引用数据类型来说,将实参引用数据类型变量的“地址值”传递给形参,方法内对形参进行修改,会影响实参的值,因为传入的是变量的地址值。
这就是 Java 中的引用数据类型的值传递机制,传递的是引用变量的“地址值”,会影响真正的变量。
习题一:
(1)定义一个Circle类,包含一个double型的radius属性代表圆的半径,一个findArea()方法返回圆的面积。
(2)考查参数的值传递。定义一个类PassObject,在类中定义一个方法printAreas(),该方法的定义如下:public void printAreas(Circle c, int time)在printAreas方法中打印输出1到time之间的每个整数半径值,以及对应的面积。
例如,time为5,则输出半径1,2,3,4,5,以及对应的圆面积。
(3)在main方法中调用printAreas()方法,调用完毕后输出当前半径值。程序运行结果如图所示。
我的首次答案:
public class ValueTransferExercise {
public static void main(String[] args) {
Circle c = new Circle();
PassObject p = new PassObject();
p.printAreas(c, 5);
}
}
class Circle {
double radius; //半径
public double findArea(){ //求面积
return Math.PI * radius * radius;
}
}
class PassObject {
public void printAreas(Circle c, int time){
//注意这里传入的第一个形参是Circle类的对象
System.out.println("Radius Area");
for (int i =1 ; i<=time; i++){
c.radius = (double) i;
System.out.println(c.radius + " " + c.findArea());
}
}
}
输出结果:
Radius Area
1.0 3.141592653589793
2.0 12.566370614359172
3.0 28.274333882308138
4.0 50.26548245743669
5.0 78.53981633974483
优化代码:用制表符\t代替空格
class PassObject {
public void printAreas(Circle c, int time){
//注意这里传入的第一个形参是Circle类的对象
System.out.println("Radius\t\t\tArea");
for (int i =1 ; i<=time; i++){
c.radius = (double) i;
System.out.println(c.radius + "\t\t\t\t" + c.findArea());
}
}
}
6.7 递归方法
6.7.1 递归的概念
( 1 \mathbf{1} 1) 递归方法:一个方法体内调用它自身。
( 2 \mathbf{2} 2) 方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
( 3 \mathbf{3} 3) 递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
6.7.2 递归与循环的对比
题目1:
计算1-100之间所有自然数的和
方法一:循环法
int num = 100;
int sum = 0;
for (int i = num; i>=0; i--){
sum += i;
}
System.out.println("方法一的和为:" + sum);
方法二:递归法
public class Recursion {
public static void main(String[] args) {
//计算1-100之间所有自然数的和,方法二
Recursion recursion = new Recursion();
System.out.println("方法二的和为:" + recursion.sum(100));
}
//计算1-100之间所有自然数的和
public int sum(int num){
if (num==1){
return 1;
}else {
return num + sum(num - 1);
}
}
}
输出结果:
方法一的和为:5050
方法二的和为:5050
题目2:
例子3:已知有一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),其中n是大于0的整数,求f(10)的值。
我的首次答案:
//例子3:已知有一个数列:f(0) = 1,f(1) =4,f(n+2)=2*f(n+1) + f(n),其中n是大于0的整数,求f(10)的值。
public class Recursion {
public static void main(String[] args) {
Recursion recursion = new Recursion();
//例子3
System.out.println("数列f(10)为:" + recursion.f(3));
}
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);
}
}
输出结果:
数列f(10)为:10497
题目3:斐波那契数列
/**
* 例子4:斐波那契数列
* 输入一个数据 n ,计算斐波那契数列 (Fibonacci)的第n个值:1 1 2 3 5 8 13 21 34 55
* 规律:一个数等于前两个数之和
* 要求:计算斐波那契数列 (Fibonacci)的第n个值,并将整个数列打印出来
*/
我的首次代码:
public class Recursion {
public static void main(String[] args) {
Recursion recursion = new Recursion();
//例子4
System.out.print("斐波那契数列为:");
recursion.printFibonacci(10);
}
/**
* 例子4:斐波那契数列
* 输入一个数据 n ,计算斐波那契数列 (Fibonacci)的第n个值:1 1 2 3 5 8 13 21 34 55
* 规律:一个数等于前两个数之和
* 要求:计算斐波那契数列 (Fibonacci)的第n个值,并将整个数列打印出来
*/
public int fibonacci(int n){
if (n==1){
return 1;
}else if (n==2){
return 1;
}else {
return fibonacci(n-1) + fibonacci(n-2);
}
}
public void printFibonacci(int n){
int[] arr = new int[n];
for (int i=0; i<n; i++){
arr[i] = fibonacci(i+1);
}
for (int i=0; i<n; i++){
System.out.print(arr[i] + "\t");
}
}
}
输出结果:
斐波那契数列为:1 1 2 3 5 8 13 21 34 55
6.8 封装性
6.8.1 封装性的内涵
( 1 \mathbf{1} 1) 把特定功能的程序封装成方法,从而无需考虑方法内部的具体代码,提高复用性,降低代码复杂度。
(
2
\mathbf{2}
2) 程序设计追求 “高内聚,低耦合”。
① 高内聚:类的内部数据操作细节自己完成,不允许外部干涉;
② 低耦合:仅对外暴露少量的方法用于使用 。
( 3 \mathbf{3} 3) 隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗地说,把该隐藏的隐藏起来,该暴露的暴露出来 。这就是封装性的设计思想。
6.8.2 对属性的封装
( 1 \mathbf{1} 1) 在实际问题中,属性是有很多其他限制的,要禁止用户直接通过"对象.属性=…" 的方式去直接赋值,通过在属性前添加权限修饰符 private 即可避免属性在 main 方法中被修改,只能在方法中被修改。
( 2 \mathbf{2} 2) 在对属性 (private) 封装后,除了提供公共的 (public) 方法中要补上"属性赋值" (setXXX)的操作外,还需补上 “属性读取” (getXXX) 的方法。
6.8.3 四种权限修饰符
( 1 \mathbf{1} 1) 封装性的体现,需要权限修饰符来配合。
( 2 \mathbf{2} 2) 四种权限修饰符从小到大排列:private、缺省(不写)、protected、public。
( 3 \mathbf{3} 3) 四种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类。
( 4 \mathbf{4} 4) 特别的,修饰类的话,只能使用:缺省、public。
6.8.4 封装性练习
题目1:
1.创建程序,在其中定义两个类:Person和PersonTest类。定义如下:
用setAge()设置人的合法年龄(0~130),用getAge()返回人的年龄。
我的首次答案:
public class Person {
private int age;
//设置合法年龄
public void setAge(int i){
if (i>=0 && i<=130){
age = i;
}else {
age = 0;
}
}
//获取年龄
public int getAge(){
return age;
}
}
public class PersonTest {
public static void main(String[] args) {
Person p = new Person();
// p.age = 1; 编译不通过,不可以直接调用age
p.setAge(23);
System.out.println("年龄为:" + p.getAge());
}
}
输出结果为:
年龄为:23
6.9 构造器(或构造方法)
6.9.1 构造器内涵
( 1 \mathbf{1} 1) 特征:
① 它具有与类相同的名称
② 它不声明返回值类型。(与声明为 void 不同)
③ 不能被 static、final、synchronized、abstract、native 修饰,不能有 return 语句返回值。
( 2 \mathbf{2} 2) 作用:创建对象;给对象进行初始化。
如:
Order o = new Order();
Person p = new Person (“Peter”,15)
如同 我们规定每个“人”一出生就必须先洗澡,我们就可以在“人” 的
构造 器 中 加入完成“洗澡”的程序代码,于是每个“人”一出生就会自
动完成“洗澡”,程序就不必再在每个人刚出生时一个一个地告诉他们
要“洗澡”了 。
6.9.2 构造器说明
( 1 \mathbf{1} 1) 如果没有显式的定义类的构造器的话,则系统默认提供一个空参的构造器。如:
Order o = new Order();
中的 “Order( )”。
( 2 \mathbf{2} 2) 构造器格式:
权限修饰符 类名(形参列表){
代码;
}
例:创建一个Person类,并定义一个构造器
public class Person {
String name;
//构造器
public Person(String n){
name = n;
}
}
这样在新建对象p时就能直接给name属性赋值。起到初始化属性的作用。
public class PersonTest {
public static void main(String[] args) {
Person p = new Person("Rick");
System.out.println(p.name);
}
}
输出结果:
Rick
( 3 \mathbf{3} 3) 同一个类中可以定义多个构造器,称作构造器的重载。
( 4 \mathbf{4} 4) 一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器。必须自己定义一个重载的空参构造器。
( 5 \mathbf{5} 5) 一个类中,至少会有一个构造器。
6.9.3 属性赋值的先后顺序
总结:
① 默认初始化
② 显式初始化
③ 构造器中初始化
④ 通过"对象.方法" 或 "对象.属性"的方式,赋值
以上操作的先后顺序:① - ② - ③ - ④
6.9.4 构造器练习
题目1:
2.1. 在前面定义的Person类中添加构造器,利用构造器设置所有人的age属性初始值都为18。
2.2. 修改上题中类和构造器,增加name属性,使得每次创建Person对象的同时初始化对象的age属性值和name属性值。
public class Person {
private String name;
private int age;
//构造器
public Person(){
age = 18;
}
public Person(String n, int i){
name = n;
age = i;
}
//设置合法年龄
public void setAge(int i){
if (i>=0 && i<=130){
age = i;
}else {
age = 0;
}
}
//获取年龄
public int getAge(){
return age;
}
//设置名字
public void setName(String n){
name = n;
}
//获取名字
public String getName(){
return name;
}
}
测试:
public class PersonTest {
public static void main(String[] args) {
Person p = new Person("Rick", 23);
System.out.println("姓名为:" + p.getName());
// p.age = 1; 编译不通过,不可以直接调用age
// p.setAge(23);
System.out.println("年龄为:" + p.getAge());
}
}
输出结果:
姓名为:Rick
年龄为:23
题目2:
编写两个类,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 void setHeight(double h){
height = h;
}
public double getBase(){
return base;
}
public double getHeight(){
return height;
}
}
测试:
public class TriAngleTest {
public static void main(String[] args) {
TriAngle t = new TriAngle(8, 6);
System.out.println("底边长:" + t.getBase());
System.out.println("高为:" + t.getHeight());
//计算三角形面积
double area = 0.5 * t.getBase() * t.getHeight();
System.out.println("面积为:" + area);
}
}
输出:
底边长:8.0
高为:6.0
面积为:24.0
题目3:
我的首次答案:
public class Student {
String name;
int age;
String school;
String major;
//构造器
public Student(){
}
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;
}
public static void main(String[] args) {
Student s1 = new Student();
Student s2 = new Student("Rick", 23);
Student s3 = new Student("Van", 45, "Fantasies College");
Student s4 = new Student("Sihang Xie", 24, "Ocean University of China", "Computer Technology");
System.out.println("姓名:"+s1.name + "\t年龄:"+s1.age + "\t学校:"+s1.school + "\t专业:"+s1.major);
System.out.println("姓名:"+s2.name + "\t年龄:"+s2.age + "\t学校:"+s2.school + "\t专业:"+s2.major);
System.out.println("姓名:"+s3.name + "\t年龄:"+s3.age + "\t学校:"+s3.school + "\t专业:"+s3.major);
System.out.println("姓名:"+s4.name + "\t年龄:"+s4.age + "\t学校:"+s4.school + "\t专业:"+s4.major);
}
}
输出:
姓名:null 年龄:0 学校:null 专业:null
姓名:Rick 年龄:23 学校:null 专业:null
姓名:Van 年龄:45 学校:Fantasies College 专业:null
姓名:Sihang Xie 年龄:24 学校:Ocean University of China 专业:Computer Technology
6.9.5 JavaBean
( 1 \mathbf{1} 1) JavaBean 是一种 Java 语言写成的可重用组件。
(
2
\mathbf{2}
2) 所谓 javaBean,是指符合如下标准的 Java 类:
① 类是公共的;
② 有一个无参的公共的构造器;
③ 有属性,且有对应的 get 、 set 方法。
( 3 \mathbf{3} 3) 用户 可以使用 JavaBean 将功能、处理、值、数据库访问和其他任何 可以用 Java 代码创造的对象进行打包,并且其他的开发者可以通过内部的 JSP页面、 Servlet 、其他 JavaBean 、 applet 程序或者应用来使用这些对象。用户可以认为 JavaBean 提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变。
6.9.6 UML类图
6.10 关键字:this的使用
6.10.1 this 的涵义
( 1 \mathbf{1} 1) this 可以用来修饰、调用:属性、方法、构造器。
( 2 \mathbf{2} 2) 个人理解:在形参名和属性名重名时用在属性名前,加以区分。
例:方法中,
//设置名字
public void setName(String name){
this.name = name;
}
其中,“this.name” 指的是当前对象的属性name ,等号右边的 “name” 是此方法的形参。
6.10.2 this调用构造器
① 我们在类的构造器中,可以显式的使用"this(形参列表)"方式,调用本类中指定的其他构造器。
② 构造器中不能通过"this(形参列表)"方式调用自己。
③ 如果一个类中有n个构造器,则最多有 n - 1构造器中使用了"this(形参列表)"。
④ 规定:"this(形参列表)"必须声明在当前构造器的首行。
⑤ 构造器内部,最多只能声明一个"this(形参列表)",用来调用其他的构造器。
6.10.3 例子
题目1:
我的首次代码:
Boy类:
public class Boy {
private String name;
private int age;
//构造器
public Boy(){
System.out.println("The boy object has been created.");
}
public Boy(String name){
this();
this.name = name;
}
public Boy(int age){
this();
this.age = age;
}
public Boy(String name, int age){
this(name);
this.age = age;
}
//方法
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setAge(int age){
if (age>=0 && age<=130){
this.age = age;
}
this.age = 0;
}
public int getAge(){
return this.age;
}
public void marry(Girl girl){
System.out.println(this.name + " marry with "+girl.getName());
}
public void shout(){
if (this.age>=22){
System.out.println("恭喜您!结婚领证了!");
}else {
System.out.println("很遗憾!您未够年龄结婚~");
}
}
//测试
public static void main(String[] args) {
Boy b1 = new Boy("Rick", 24);
Girl g1 = new Girl("Rain", 22);
System.out.println(b1.getName() + ", " + b1.getAge());
System.out.println(g1.getName() + ", " + g1.getAge());
b1.marry(g1);
b1.shout();
g1.marry(b1);
}
}
Girl类:
public class Girl {
private String name;
private int age;
//构造器
public Girl(){
System.out.println("The girl object has been created.");
}
public Girl(String name){
this();
this.name = name;
}
public Girl(int age){
this();
this.age = age;
}
public Girl(String name, int age){
this(name);
this.age = age;
}
//方法
public void setName(String name){
this.name = name;
}
public String getName() {
return this.name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age>=0 && age<=130){
this.age = age;
}
this.age = 0;
}
public void marry(Boy boy){
System.out.println("I want to marry with "+boy.getName());
boy.marry(this);
}
public int compare(Girl girl){
return this.age - girl.getAge();
}
}
测试:
//测试
public static void main(String[] args) {
Boy b1 = new Boy("Rick", 24);
Girl g1 = new Girl("Rain", 22);
System.out.println(b1.getName() + ", " + b1.getAge());
System.out.println(g1.getName() + ", " + g1.getAge());
b1.marry(g1);
b1.shout();
g1.marry(b1);
}
输出:
The boy object has been created.
The girl object has been created.
Rick, 24
Rain, 22
Rick marry with Rain
恭喜您!结婚领证了!
I want to marry with Rick
Rick marry with Rain
6.11 关键字:package、import
6.11.1 package关键字
( 1 \mathbf{1} 1) package 语句作为 Java 源文件的第一条语句,指明该文件中定义的类所在的包。 若缺省该语句,则指定为无名包 。它的格式为:
package 顶层包名.子包名;
( 2 \mathbf{2} 2) 包对应于文件系统的目录, package 语句中,用 “.” 来指明包 (目录) 的层次;每"."一次,就代表一层文件目录。
( 3 \mathbf{3} 3) 包通常用小写单词标识。通常使用所在公司域名的倒置:
cn.edu.ouc.xxx(包名)
( 4 \mathbf{4} 4) 同一个包下,不能命名同名的接口、类。不同的包下,可以命名同名的接口、类。
( 5 \mathbf{5} 5) JDK中主要的包:
( 6 \mathbf{6} 6) MVC设计模式:
以后的大项目的包,也要遵循这种结构来创建。
6.11.2 import 关键字
( 1 \mathbf{1} 1) import的作用:为使用定义在不同包中的 Java 类,需用 import 语句来引入 指定包层次下 所需要的类或全部类 。 import 语句告诉编译器到哪里去寻找类。
格式:
import 包名.类名;
( 2 \mathbf{2} 2) 可以使用"xxx.*****"的方式,表示可以导入xxx包下的所有结构。
( 3 \mathbf{3} 3) 如果使用的类或接口是java.lang包下定义的,则可以省略import结构.
( 4 \mathbf{4} 4) 如果在源文件中,使用了不同包下的同名的类,则必须至少有一个类需要以全类名的方式显示。
Date date = new Date();
java.sql.Date date1 = new java.sql.Date(5243523532535L);
( 5 \mathbf{5} 5) 使用"xxx.*****"方式表明可以调用xxx包下的所有结构。但是如果使用的是xxx子包下的结构,则仍需要显式导入。