[TOC]
Java基础部分 ​
主要包括Java语法的:
- 基础语法
- 流程控制
这两个阶段的作业。
Scanner基础使用练习 ​
键盘输入某个学生的三门学科(数学,语文,英语)成绩,然后分别求出三科成绩的平均分,最低分,最高分,三科的总分。
注:
代码直接写在main()方法中即可,
注意录入数据的数据类型。
javapublic static void main(String[] args) { System.out.println("依次输入语文、数学、英语成绩:"); Scanner sc = new Scanner(System.in); Double sum = 0.0; Double scoreHighest = 0.0; Double scoreLowest = Double.MAX_VALUE; for (int i = 0; i < 3; i++) { double score = sc.nextDouble(); sum += score; if(scoreHighest < score) scoreHighest = score; if(scoreLowest > score) scoreLowest = score; } System.out.printf("最高分:%f, 最低分:%f,平均分:%f",scoreHighest,scoreLowest,sum / 3); }
Scanner基础使用练习 ​
使用Scanner依次接收以下三种数据类型的值:
- int类型
- String类型
- double类型
接收三个值后,再计算:
- 求和:int + double
- 拼接字符串:int + double + String
分别接收计算结果后,输出该计算结果。
注:
使用Scanner时,注意不要混用next系列和nextLine方法。(原因可以查看详细文档说明)
代码直接全部写在main方法中即可。
javapublic static void main(String[] args) { System.out.println("依次输入int,String,double类型:"); Scanner sc = new Scanner(System.in); String n = sc.nextLine(); String s = sc.nextLine(); String d = sc.nextLine(); System.out.printf("%d\t%s\t%f\n",Integer.parseInt(n),s,Double.parseDouble(d)); }
Scanner综合练习 ​
模拟账号登录功能
1.用户名(String):admin
2.密码(String): 123456
3.使用scanner进行输入, 有3次机会可以重新输入, 用户名密码匹配成功提示登陆成功并结束, 否则提示登陆失败还剩几次机会 , 一旦3次都失败提示锁定账户结束程序.
public static void main(String[] args) {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
System.out.println("**********登录***********");
System.out.println("输入用户名:");
Scanner sc = new Scanner(System.in);
String userName = sc.nextLine();
System.out.println("输入密码:");
String userPass = sc.nextLine();
if(userName.equals("admin") && userPass.equals("123456")){
System.out.println("登陆成功!");
break;
}else{
if(i == 2){
System.out.println("输入错误!账户已被锁定");
break;
}
System.out.printf("输入错误!还剩%d次机会\n",3 - i - 1);
}
}
}
}switch练习 ​
利用switch语句输出每个月份对应的季节:
- 12、1、2:冬季
- 3、4、5:春季
- 6、7、9:夏季
- 9、10、11:秋季
注:合理使用case穿越,注意命名规范
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int month = sc.nextInt();
switch (month) {
case 12:
case 1:
case 2:
System.out.println("冬季");
break;
case 3:
case 4:
case 5:
System.out.println("春季");
break;
case 6:
case 7:
case 8:
System.out.println("夏季");
break;
case 9:
case 10:
case 11:
System.out.println("秋季");
break;
default:
break;
}
}
}if多分支练习 ​
键盘录入某个人的工资收入,求他应该缴纳的个人所得税以及最后得到的工资。
也就是说需要求两个值:
- 缴纳的个税
- 到手工资
个人所得税的计算方法是:
应纳税额 = 应纳税所得额 * 税率 - 速算扣除数 (速算扣除额已经扣除了重复计算的部分)
其中,应纳税所得额 = 工资薪金所得-2000(即工资超过2000的部分才需要缴税)
针对不同的收入人群,需要缴纳个税的税率是不同的,详参下列表格:
级数 应纳税所得额 税率(单位:%) 速算扣除数 1 不超过500元的 5 0 2 超过500元但是不超过2000的 10 25 3 超过2000元但是不超过5000元的 15 125 4 超过5000元但是不超过20000元的 20 375 5 超过20000元但是不超过40000元的 25 1375 6 超过40000元但是不超过60000元的 30 3375 7 超过60000元但是不超过80000元的 35 6375 8 超过80000元但是不超过100000元的 40 10375 9 超过100000元的 45 15375
注:
- 代码直接写在main()方法中即可,

public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
double salary = sc.nextDouble();
double taxAble = salary - 2000;//应纳税所得额
double taxRate = 0;//税率
double deduction = 0;//速算扣除数
if (taxAble <= 0) {
System.out.println("应该缴纳个税0,到手工资" + salary);
return;
}
if (taxAble <= 500) {
taxRate = 5;
deduction = 0;
} else if (taxAble <= 2000) {
taxRate = 10;
deduction = 25;
} else if (taxAble <= 5000) {
taxRate = 15;
deduction = 125;
} else if (taxAble <= 20000) {
taxRate = 20;
deduction = 375;
} else if (taxAble <= 40000) {
taxRate = 25;
deduction = 1375;
} else if (taxAble <= 60000) {
taxRate = 30;
deduction = 3375;
} else if (taxAble <= 80000) {
taxRate = 35;
deduction = 6375;
} else if (taxAble <= 100000) {
taxRate = 40;
deduction = 10375;
} else {
taxRate = 45;
deduction = 15375;
}
double res1 = taxAble * taxRate * 0.01 - deduction;
System.out.printf("应该缴纳个税%f,到手工资%f", res1, salary - res1);
}循环相关练习题 ​
无论是for循环、while循环还是do...while循环,它们的本质都是一致的,使用时也没有特别的差异。下列给出几道简单的练习题,大致做一做。实际开发中,要根据实际需求,灵活使用循环。
- 计算 2 + 4 + 6 + … + 100 的值。
- 计算2000年1月1日到2008年1月1日相距多少天。(注意闰年)
- 循环输入一个不为0的数进行累加,直到输入的数字为0,结束循环并最后输出累加的结果。
注:
代码直接写在main()方法中即可,
使用什么循环都行,实现功能就行。
javapublic static void main(String[] args) { //计算 2 + 4 + 6 + … + 100 的值。 int sum = 0; for (int i = 2; i <= 100 ; i+=2) { sum += i; } System.out.println(sum); }javapublic static void main(String[] args) { //计算2000年1月1日到2008年1月1日相距多少天。(注意闰年) int sum = 0; for (int i = 2000; i < 2008; i++) { if(i % 400 == 0 || (i % 4 == 0 && i % 100 != 0)) { //是闰年,366天 sum += 366; }else sum += 365; } System.out.println(sum); }javapublic static void main(String[] args) { Scanner sc = new Scanner(System.in); int sum = 0; while(true){ int num = sc.nextInt(); if(num == 0) break; sum += num; } System.out.println(sum); }
方法 ​
主要包括Java语法的:
- 方法
- 方法的重载
- Junit
- Debug模式练习
基础语法练习 ​
方法是Java代码中最常用的语法结构,这里对方法的语法做一下练习。
在使用方法或者想要写一个方法时,首先要考虑的是方法需要的参数是什么数据类型,需要几个(形式参数),再考虑方法需要返回什么结果(返回值类型)。在调用方法时,需要搞清楚具体传入什么数值(实际参数)。
完成如下练习题,要求提取出必要的方法,然后在main方法中调用测试:
使用for循环遍历n以内的奇数,并计算所有的奇数的和并输出。
javapublic static int func(int num) { //使用for循环遍历n以内的奇数,并计算所有的奇数的和并输出。 int sum = 0; for (int i = 1; i < num; i++) { if(i % 2 != 0){ sum += i; System.out.println(i); } } return sum; }打印倒三角形,并且行数(line)是可控制的。比如我想打印6行的倒三角形,则如下图:
****** ***** **** *** ** *javapublic static void func() { Scanner sc = new Scanner(System.in); int row = sc.nextInt(); for (int i = 0; i < row; i++) { for (int j = row - i; j > 0; j--) { System.out.print("*"); } System.out.println(); } }根据键盘录入打印nn乘法表,n是可变的。
javapublic static void func() { Scanner sc = new Scanner(System.in); int row = sc.nextInt(); for (int i = 1; i <= row; i++) { for (int j = 1; j <= i; j++) { System.out.printf("%d * %d = %d\t",j,i,i*j); } System.out.println(); } }键盘录入一个int数字,判断它是奇数还是偶数。
javapublic static void func() { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); if(n % 2 == 0) System.out.println("偶数"); else System.out.println("奇数"); }根据键盘录入打印n行m列井号,n和m是可变的。
javapublic static void func() { Scanner sc = new Scanner(System.in); int row = sc.nextInt(); int cul = sc.nextInt(); for (int i = 0; i < row; i++) { for (int i1 = 0; i1 < cul; i1++) { System.out.print("#"); } System.out.println(); } }(扩展题)输出前n个数当中的所有素数,并统计个数。
javapublic static void func() { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int count = 0;//素数的个数 for (int i = 2; i < n; i++) { if(isPrime(i)){ System.out.println(i); count++; } } System.out.println("前" + n + "个数中有" + count + "个素数"); } public static boolean isPrime(int n){ for (int i = 2; i <= Math.sqrt(n); i++) { if(n % i == 0){ return false; } } return true; }
完成控制台项目阶段一 ​
详细说明,见文档说明。
Java数组 ​
主要包括Java语法的:
- 数组的基本使用
- JVM内存模型
- 可变参数
- 值传递
- ....
数组基本使用练习 ​
- 定义一个double数组用来存放学生成绩,然后键盘录入10位同学的成绩并存入数组,求这10位同学成绩的平均值。
- 定义一个String数组,输出该数组的长度,并用多种方式遍历打印数组元素(常见的方式遍历即可)
注:
请合理使用方法,不要胡子眉毛一把抓把代码全部写在main方法里。
javapublic static void main(String[] args) { double[] scores = new double[10]; System.out.println("输入10个成绩"); Scanner sc = new Scanner(System.in); double sum = 0; for (int i = 0; i < 10; i++) { scores[i] = sc.nextDouble(); } System.out.println("平均成绩:" + getAvg(scores)); } static double getAvg(double[] scs){ double sum = 0; for (double sc : scs) { sum += sc; } return sum / scs.length; }数组遍历的方式,比如for、增强for或者使用工具类等等。
javapublic static void main(String[] args) { String[] strs = {"one","two","three","four","five","six","seven","eight"}; System.out.println("数组长度:" + getLength(strs)); traverse1(strs); traverse2(strs); traverse3(strs); String[] strs1 = null;//null数组 System.out.println("数组长度:" + getLength(strs1)); traverse1(strs1); traverse2(strs1); traverse3(strs1); String[] strs2 = new String[]{};//空数组 System.out.println("数组长度:" + getLength(strs2)); traverse1(strs2); traverse2(strs2); traverse3(strs2); } static int getLength(String[] strs){ if(strs == null) return -1;//表示null数组 return strs.length; } //for循环 static void traverse1(String[] strs){ if(strs == null){ System.out.println("null数组"); return; } if(strs.length == 0){ System.out.println("空数组"); return; } for (int i = 0; i < 8; i++) { System.out.print(strs[i] + "\t"); } System.out.println(); } //增强for static void traverse2(String[] strs){ if(strs == null){ System.out.println("null数组"); return; } for(String s : strs){ System.out.print(s + "\t"); } System.out.println(); } //工具类 static void traverse3(String[] strs){ if(strs == null){ System.out.println("null数组"); return; } System.out.println(Arrays.toString(strs)); }数组的逆序
javapublic static void main(String[] args) { int[] nums = {1,2,3,4,5,6,7,8}; //数组的逆序 reverse(nums); System.out.println(Arrays.toString(nums)); reverse1(nums); System.out.println(Arrays.toString(nums)); } static void swap(int[] nums,int i,int j){ int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; } static void reverse(int[] nums){ int len = nums.length; for (int i = 0; i < len / 2; i++) { swap(nums,i,len - 1 - i); } } static void reverse1(int[] nums){ int len = nums.length; for (int i = 0,j = len - 1; i < j; i++,j--) { swap(nums,i,j); } }
数组基本使用练习 ​
现在你去参加歌唱比赛。有10个评委打分,规则是去掉最高分和最低分,求平均分是最终成绩。请编码计算出你的最终成绩~
注:
请合理使用方法,不要胡子眉毛一把抓把代码全部写在main方法里。
最高分和最低分可能有多个,但只需要去掉其中一个即可(也就是说平均成绩要除以8)
建议给出下面的三个方法:
- 求数组中元素最大值的方法
- 求数组中元素最小值的方法
- 求数组中元素平均分的方法
当然如果你有自己的想法,也可以用自己的方式去实现功能。
java//思路想复杂了,直接求最大值最小值,sum减去最大值和最小值就行了 public static void main(String[] args) { double[] scores = {90,90,90,90,90,90,90,90,90,90}; System.out.println("平均分:" + getAverage(scores)); } //得到最大值的下标 static int getHIndex(double[] scs){ double max = scs[0]; int index = 0; for (int i = 1; i < scs.length; i++) { if(max < scs[i]){ max = scs[i]; index = i; } } System.out.println("最高分:" + max); return index; } //得到最小值的下标 static int getLIndex(double[] scs){ double min = scs[0]; int index = 0; for (int i = 1; i < scs.length; i++) { if(min > scs[i]){ min = scs[i]; index = i; } } System.out.println("最低分:" + min); return index; } //得到平均分 public static double getAverage(double[] scs){ double sum = 0; int len = scs.length; int h = getHIndex(scs); int l = getLIndex(scs); //如果最高和最低一样,说明全一样 if(h == l) return scs[h]; for (int i = 0; i < len; i++) { if(i == h || i == l) continue; sum += scs[i]; } return sum / (len - 2); }
数组排序(扩展) ​
随意给出一个长度为10的int数组,然后升序排列它。
注:
如果想自己写排序实现,可以使用最简单的冒泡排序或者选择排序。(不会就百度一下)
排序对于数组而言是非常常见的操作,Java源码工具类中,早有对应的实现。请百度查询一下,不需要了解它具体怎么实现的,会用即可。(百度并学习是非常重要的能力,我们不可能在课堂上学会所有的知识)
javapublic static void main(String[] args) { int[] scores = {92,60,50,44,99,30,8,20,40,96}; bubbleSort(scores); int[] scores2 = {9,60,232,44,99,55,2,23,49,964}; selectionSort(scores2); } private static void swap(int[] nums, int m,int n){ int tmp = nums[m]; nums[m] = nums[n]; nums[n] = tmp; } private static void selectionSort(int[] scs) { int len = scs.length; for (int i = 0; i < len - 1; i++) {//交换len - 1次 int min = i;//最小元素的下标 for (int j = i; j < len; j++) {//找[i,len)之间最小元素的下标 if(scs[min] > scs[j]) min = j; } //交换最小元素和第i个元素 if (i != min) { swap(scs,i,min); } } System.out.println(Arrays.toString(scs)); } private static void bubbleSort(int[] scs) { int len = scs.length; for (int i = 0; i < len - 1; i++) {//冒泡多少趟 for (int j = 0; j < len - i - 1; j++) {//每次冒泡交换的次数 if(scs[j] > scs[j + 1]){ swap(scs,j,j+1); } } } System.out.println(Arrays.toString(scs)); }
数组基础练习——除以首元素 ​
定义一个double类型的数组,让数组中每个元素(包括首位元素)都除以首位元素,得到的结果过作为该位置上的新元素。请在原先数组数组上操作,并打印新数组。 例如数组[ 2.0 , 4.0 , 6.0 , 4.0 ] 经过方法运算得到新数组 [ 1.0 ,2.0 , 3.0 ,2.0 ]
注:注意元素取值的变化。
public static void main(String[] args) {
double[] nums = {2.0 , 4.0 , 6.0 , 4.0};
int len = nums.length;
double factor = nums[0];
if(factor == 0) return;
for (int i = 0; i < len; i++) {
nums[i] = nums[i] / factor;
}
System.out.println(Arrays.toString(nums));
}数组综合练习——删除元素 ​
举例,对于String数组["abc", "123", "123", "123", "666", "777"] 将其中的元素"123"全部去掉,就得到了新数组["abc","666", "777"],一共去掉了3个元素。
注:
这里说的去掉,不是指用0/null等默认值替代原先的元素,而是指真正的删除掉。
思考:同一个数组能不能实现这个功能?如果不能,应该怎么完成呢?
javapublic static void main(String[] args) { String[] strs = {"abc", "123", "123", "123", "666", "777"}; int len = strs.length; int[] index = new int[len];//记录符合目标的数组的下标 int count = 0;//要保留的个数 for(int i = 0;i < len;i++){ if(strs[i].equals("123") == false){ index[count++] = i; } } String[] newStrs = new String[count];//最后符合要求的新数组 for (int i = 0; i < count; i++) { newStrs[i] = strs[index[i]]; } System.out.println(Arrays.toString(newStrs)); System.out.println("一共去掉" + (len - count) + "个元素"); }
数组元素的删除,修改,查找 ​
根据提供的代码,完成人名数组的删除、修改和查找方法。
其中:
删除人名,给出一个要删除的目标元素,用null替换它,表示删除
修改人名,需要明确指出修改哪个人名,以及修改成什么。
查找人名,找到后返回目标人名在数组中的位置(下标)
可以直接在我的代码中,直接写完方法,直接测试。
也可以自己新写一个类,重新定义数组,定义方法,然后测试
public static void main(String[] args) {
String[] stus = {"杜梦颖","任晓宇","景天老师"};
System.out.println(findStu(stus, "杜梦颖"));
updateStu(stus,"杜梦颖","杜梦");
System.out.println(Arrays.toString(stus));
delStu(stus,"杜梦");
System.out.println(Arrays.toString(stus));
}
private static void delStu(String[] stus, String target) {
int index = findStu(stus,target);
if(index == -1) return;
stus[index] = null;
}
private static void updateStu(String[] stus, String target,String newName) {
int index = findStu(stus,target);
if(index == -1) return;
stus[index] = newName;
}
private static int findStu(String[] stus, String target) {
for (int i = 0; i < stus.length; i++) {
if(stus[i].equals(target))
return i;
}
return -1;//表示没找到
}数组元素添加(扩展) ​
利用数组元素的拷贝,实现真正意义上的在一个int数组中新增一个元素。新增元素的位置可自由决定,不要求排序,实现功能即可。
public static void main(String[] args) {
int[] originalNums = {92,60,50,44,99,30,8,20,40,96};
int len = originalNums.length;
int[] newNums = new int[len + 1];
System.out.println("在下标为几的位置上插入几");
Scanner sc = new Scanner(System.in);
int index = sc.nextInt();
int target = sc.nextInt();
for (int i = 0,j = 0; i < len; i++,j++) {
if(j == index){//j控制newNums的下标,i控制original的下标
newNums[j] = target;
j++;
}
newNums[j] = originalNums[i];
}
System.out.println(Arrays.toString(newNums));
}数组综合练习——合并数组(扩展) ​
首先准备两个数组(简单起见,用两个int数组即可)
然后写方法,将这两个数组合并。
注:
数组合并也是很常见的操作,实际开发中有很多现成的手段去完成它。由于有些手段是超纲的,所以这里不再细表。
如果后续工作中碰到了,可以再百度学习一下。目前就先考虑自己手写实现就可以了。
javaprivate static int[] mergeNums(int[] nums1, int[] nums2) { int len = nums1.length + nums2.length; int[] newNums = new int[len]; int index = 0; for(int n : nums1){ newNums[index++] = n; } for(int n : nums2){ newNums[index++] = n; } return newNums; }
值传递练习 ​
Java有且仅有值传递,Java中的方法不能直接改变实参本身。
对于下列代码:
public static void main(String[] args) { int a = 10; int b = 20; method(a, b); System.out.println("a = " + a); System.out.println("b = " + b); } public static void method(int a, int b) { a *= 2; b *= 2; }思考以下问题:
对于以上Java代码,method()方法可以把a,b的取值变为原先的2倍吗?如果不能,为什么?
不能,因为基本数据类型传参只是值传递,不能改变实参
上述main方法不变,修改method方法实现,让程序输出:
- a = 20
- b = 40
如何实现呢?
注:这是个脑筋急转弯,不会的可以互相问问或者查一查。
在method中添加打印语句
可变参数练习 ​
可变参数的本质是数组,完成下列需求:
求不限定个数参数的最大值(要求使用可变参数,使用int类型即可)
```java
public static int getMax(int...nums) {
int max = Integer.MIN_VALUE;
for(int n : nums){
if(max < n)
max = n;
}
return max;
}
```
判断2个字符串对象相等 ​
对于任意两个字符串对象的引用str1,str2。
怎么判断这两个字符串对象的内容相同呢?
注:如果有一个引用等于null,那么另一个引用也等于null,也可算内容相同
思考一下,写出你的答案。
private static boolean comStr(String str1, String str2) {
if(str1 == null && str2 == null) return true;
int len = str1.length();
if(len != str2.length()) return false;
for (int i = 0; i < len; i++) {
if(str1.charAt(i) != str2.charAt(i))
return false;
}
return true;
}二维数组和递归 ​
主要涵盖内容:
- 二维数组
- 递归
练习使用二维数组 ​
二维数组不是必须要掌握的知识点,对于JavaWeb实际开发而言,它也没有太大的作用。
但就像嵌套for循环一样,二维数组毕竟比一维数组更复杂一些,但本质不变。练习二维数组,可以锻炼逻辑思维能力,建议大家去自学一下,并且在时间充裕的情况下,完成下列两道题。
题目1 ​
某公司该年度,每个季度的销售额(单位:万元)如下: 第一季度:30,66, 48 第二季度:10, 33, 20 第三季度: 10,99,103 第四季度: 9,18,27 请使用二维数组存储数据 并计算: 1,每个季度平均销售额 2,年度销售总额
public static void main(String[] args) {
int[][] volume = new int[4][3];
volume[0] = new int[]{30,66,48};
volume[1] = new int[]{10,33,20};
volume[2] = new int[]{10,99,103};
volume[3] = new int[]{9,18,27};
//可以放到for循环里
System.out.println("季度平均销售额:"+ getAverage(volume[0]));
System.out.println("季度平均销售额:"+ getAverage(volume[1]));
System.out.println("季度平均销售额:"+ getAverage(volume[2]));
System.out.println("季度平均销售额:"+ getAverage(volume[3]));
System.out.println("年度销售总额:" + getSum(volume));
}
static double getAverage(int[] nums){
int len = nums.length;
int sum = 0;
for(int n : nums)
sum += n;
return sum / 3.0;
}
static int getSum(int[][] matrix){
int len = matrix.length;
int sum = 0;
for (int i = 0; i < len; i++) {
for (int j = 0; j < matrix[i].length; j++) {
sum += matrix[i][j];
}
}
return sum;
}题目2 ​
已知有3个班级(一班,二班,三班)分别有3人,2人,5人 键盘录入每个班级的学生的成绩,并使用二维数组存储数据 然后计算: 每个班级的平均成绩,每个班级中的最高成绩和最低成绩,并输出
public static void main(String[] args) {
int[][] scores = new int[3][];
scores[0] = new int[3];
scores[1] = new int[2];
scores[2] = new int[5];
Scanner sc = new Scanner(System.in);
for (int i = 0; i < 3; i++) {
int count = scores[i].length;
System.out.printf("输入%d班%d人的成绩:\n",i + 1,count);
for (int i1 = 0; i1 < count; i1++) {
scores[i][i1] = sc.nextInt();
}
}
for (int i = 0; i < 3; i++) {
System.out.println(i + 1 + "班");
System.out.println(Arrays.toString(scores[i]));
System.out.println("平均成绩:" + getAverage(scores[i]));
int[] hl = getHL(scores[i]);
System.out.println("最高成绩" + hl[1]);
System.out.println("最低成绩" + hl[0]);
}
}
static double getAverage(int[] nums){
int len = nums.length;
int sum = 0;
for(int n : nums)
sum += n;
return sum / 3.0;
}
static int[] getHL(int[] nums){
int[] hl = new int[2];
hl[0] = Integer.MAX_VALUE;
hl[1] = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++) {
if(hl[0] > nums[i]) hl[0] = nums[i];
if(hl[1] < nums[i]) hl[1] = nums[i];
}
return hl;
}练习使用递归 ​
递归在实际开发中用途并不广泛,但我们仍有必要学习它的基本使用。
这道题目如果想不明白,那就百度一下吧。
使用递归,把十进制正整数(N>=0)转换成二进制数 public static void main(String[] args) {
int n = 128;
StringBuilder s = new StringBuilder();
getBinary(n,s);
System.out.println(s);
}
static void getBinary(int n,StringBuilder s) {
if (n == 0)
return;
getBinary(n / 2,s);
s.append(String.valueOf(n % 2));
}OOP1——基础部分 ​
主要包括面向对象第一部分知识点:
- 对象与类
- this关键字
- 构造器
构造器之前的知识。
对象与类基础语法练习题 ​
设计物品(Item)类
它具有以下属性:
- 名字
- 价格
- 类别
它具有以下行为:
售卖。该方法需要传入一个参数表示购买使用的金钱,如果金钱足够则打印"xx物品被卖出"的信息,并将找零作为返回值。
否则打印"金钱不足,购买失败",并将"-1"作为返回值表示购买失败。
类定义完毕后,试着创建三个对象并使用"对象名点"完成属性赋值:
- "手机类的Samsung Galaxy S21 8000元"
- "家具类的海尔冰箱 3000元"
- "日用品类的海飞丝洗发水 30元"
然后自行调用一下售卖方法,测试一下。
注:
建议用一个public class作为测试类,用于创建对象,调用方法,访问属性等。
具体类(比如本题中的Item)的定义,建议在public class下面定义非public class,这样比较方便快捷。
- java
public class Main { public static void main(String[] args) { Item phone = new Item(); Item fridge = new Item(); Item shapoo = new Item(); phone.catagory = "手机"; phone.name = "Samsung Galaxy"; phone.price = 8000; phone.buy(10000); fridge.catagory = "家具"; fridge.name = "海尔冰箱"; fridge.price = 3000; fridge.buy(10000); shapoo.catagory = "日用品"; shapoo.name = "海飞丝洗发水"; shapoo.price = 30; shapoo.buy(10); }
} class Item{ String name; double price; String catagory;
double buy(double pay){
if(pay >= price){
System.out.println( name+"被卖出");
return price - pay;
}
else System.out.println("金钱不足,购买失败");
return -1;
}
}
## 构造方法和this关键字练习题
> 定义一个Teacher类:
>
> 包含4个成员变量 String name, int age, String gender, int teacherId
>
> 然后定义5个构造方法:
>
> 1. 无参构造
> 2. name单参构造方法
> 3. name和teacherId双参构造方法
> 4. name,age和gender的三参构造器
> 5. name,age,gender和teacherId的四参构造器(要求用this调用已存在的三参构造器)
>
> 最后再定义一个成员方法:
>
> print():用于打印这个Teacher类对象的基本信息。
>
> 回答下列问题:
>
> 1. 在上面5个构造方法的基础上,再定义一个构造方法单独给gender赋值,能够做到吗?为什么?
>
> 不能
>
> 2. 定义完上述结构后,再定义一个成员方法setGender,用于给gender赋值。要求方法的形参就是String gender,这个方法如何写呢?
>
> 定义完这个类后,创建两个Teacher对象分别为t1和t2
>
> 1. 要求t1对象的四个成员变量name, age, gender, teacherId的值分别为"张三"、18、"男"、 1
>
> 2. 要求t2对象的四个成员变量name, age, gender, teacherId的值分别为"李四"、25、"女"、 2
>
> <font color=red>**注意在使用构造器时,对应位置的实参和形参要一一对应。**</font>
>
> 最后分别用两个对象调用print方法,两次调用方法输出的结果相同吗?为什么?
```java
public class Main {
public static void main(String[] args) {
Teacher te1 = new Teacher("张三",18,"男",1);
Teacher te2 = new Teacher("李四",25,"女",2);
te1.print();
te2.print();
}
}
class Teacher{
String name;
int age;
String gender;
int teacherId;
public Teacher(){
}
public Teacher(String name){
this.name = name;
}
public Teacher(String name,int id){
this.name = name;this.teacherId = id;
}
public Teacher(String name,int id,String gend){
this.name = name;this.teacherId = id;
this.gender = gend;
}
public Teacher(String name,int id,String gend,int age){
this.name = name;this.teacherId = id;
this.age =age;this.gender = gend;
}
public void setGender(String gender){
this.gender = gender;
}
public void print() {
System.out.println("name='" + name + "\nage=" + age +
"\ngender='" + gender + '\'' +
"\neacherId=" + teacherId);
}
}对象数组练习 ​
定义一个Student类:
成员变量:String name,int stuId,String gender,int age,double score(表示学生Java考试成绩)
构造器:无参构造器,全参构造器等,自由发挥。
成员方法:print(),打印对象所有属性的取值。
类定义完毕后,创建五个Student对象存入Student对象数组中,完成以下两个操作:
- 遍历输出每个学生的属性信息(遍历对象调用print()方法)
- 求成绩平均值。
注:
- 数组既可以存储基本数据类型(的值),也可以存储引用数据类型(的引用)。它们的使用没有本质区别。
- 可以考虑提取方法实现功能,不要把所有代码一股脑写在main方法中。
public class Main {
public static void main(String[] args) {
Student stu1 = new Student("小明", 1, "男", 3, 90);
Student stu2 = new Student("小红", 2, "女", 5, 77.5);
Student stu3 = new Student("小黄", 3, "男", 3, 88.88);
Student stu4 = new Student("小绿", 4, "男", 7, 60);
Student stu5 = new Student("小花", 5, "女", 9, 80);
Student[] stus = {stu1,stu2,stu3,stu4,stu5};
for (Student stu : stus) {
stu.print();
}
getAvg(stus);
}
static void getAvg(Student...stus){
int len = stus.length;
double sum = 0;
for (Student stu : stus) {
sum+=stu.score;
}
System.out.println("平均分:" + sum / len);
}
}
class Student {
String name;
int stuId;
String gender;
int age;
double score;
public Student() {
}
public Student(String n, int id, String gen, int a, double sc) {
name = n;
stuId = id;
gender = gen;
age = a;
score = sc;
}
public void print() {
System.out.println("Student{" +
"name='" + name + '\'' +
", stuId=" + stuId +
", gender='" + gender + '\'' +
", age=" + age +
", score=" + score +
'}');
}
}OOP1——高级部分 ​
主要包括面向对象第一部分知识点:
- static
- 代码块
- 访问权限修饰符
- ...
包含OOP1后部分所有知识点
static基础语法练习 ​
当类中的成员变量被static修饰时,那么它就是一个静态成员变量。当类中的成员方法被static修饰时,那么它就是一个静态成员方法。
普通成员(变量和方法)必须创建对象才能够访问,它们是属于对象的的。静态成员(变量和方法) 属于类,通过"类名点"就可以访问,它们是属于类的。
静态成员变量还有一个非常重要特性:由于类加载只有一次,所以静态成员变量在类全局都是唯一的!
现在你需要完成以下基础语法练习:
编写一个类模拟银行账户(Account)的功能,包含的属性有"账户名(ID)"、"密码"、"账户余额"、"储蓄年利率"等。 其中:
- 账号名:要求每个账户的账户名都是唯一的,且按照固定格式自动生成:
- 第一位账户的账户名是"10001"
- 其后账户的账户名依次加1。比如第二位账户的账户名就是"10002",第三位就是"10003".....
- 密码:新建账户时,可以选择自定义密码,也可以选择使用默认密码:"000000"(6个0)
- 账户余额:可以选择在新建账户时预设一个值,也可以保持默认值0
- 储蓄年利率:由银行设置,和账户本身没有关系。你可以将它设置为"0.3%"(活期年利率一般都非常低,聊胜于无)
定义完属性后,再给出两个成员方法:
打印账户所有属性。
根据传入的年限和储蓄年利率,计算本息合计后的余额。
注:计算利息时简单点,不考虑利滚利。即利息 = 利率 * 时间 * 本金
提示:
账户ID是独属于某个账户的,它肯定是一个成员变量。但是它的赋值却是在一个初始值的基础上,创建一个对象就+1,这需要一个不受创建对象影响的变量。
储蓄年利率肯定不是属于账户对象的。
javapublic class test2 { public static void main(String[] args) { Account ac1 = new Account(); Account ac2 = new Account(); ac1.print(); ac2.print(); ac1.getRemain(1, 0.3); } } class Account { static int defaultId = 10000; int id = 0; String pwd = "000000"; double remain = 0;//余额 static final double PERCENT = 0.3;//0.3% public Account() { id = ++defaultId; System.out.println("账户名:" + id); Scanner sc = new Scanner(System.in); System.out.println("输入密码(没有输入则设置为默认值):"); String pass = sc.nextLine(); if (pass.length() != 0) pwd = pass; System.out.println("设置余额(没有输入则设置为默认值):"); String rem = sc.nextLine(); if (rem.length() != 0) remain = Double.parseDouble(rem); } public void print() { System.out.println("Account{" + "id=" + id + ", pwd='" + pwd + '\'' + ", remain=" + remain + ", PERCENT=" + PERCENT + '}'); } public void getRemain(int year, double percent) { //计算利息时简单点,不考虑利滚利。即利息 = 利率 * 时间 * 本金 //余额 = 本金 + 利息 double interest = percent * year * remain * 0.01; double total = remain + interest; System.out.println("total = " + total); } }
猜数小游戏 ​
百度和各种搜索引擎就是生产力,尝试自己解决以下问题:
先生成一个随机数(1~100之间的整数),再键盘输入猜测的数 如果猜的数大了或者小了,给出提示,继续猜,直到猜中为止。
注:Java如何生成随机数,自己百度查一下非常简单。这点学习能力还是需要具备的。
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Random rd = new Random();
int target = rd.nextInt(100) + 1;//[0,100) + 1
while(true){
int guess = sc.nextInt();
if(guess == target){
System.out.println("恭喜你,猜对了!");
break;
}else if(guess > target){
System.out.println("猜大了");
}else System.out.println("猜小了");
}
}自己编写数组工具类 ​
工具类:指的是类中方法全部是静态方法的类,工具类在使用时无需创建对象(静态方法无需对象调用)
以下方法的实现,都是我们上课写过的代码,如果觉得自己很熟练了,直接抄代码即可
如果觉得不熟悉或者想再练习一遍,再写一遍也可以~
写一个数组的工具类ArrayTool, 要求提供如下方法:
遍历,求最大值,最小值,逆置数组元素
查询(在数组中查找指定元素,若不存在,待查找元素返回-1,若存在返回元素在数组中首次出现的位置)
查询(在数组中查找指定元素,若不存在,待查找元素返回-1,若存在返回元素在数组中最后一次出现的位置)public class Main {
public static void main(String[] args) {
int[] nums = {1, 2, 3, 4, 5, 6, 2, 7};
ArraysTool.traversal(nums);
System.out.println("ArraysTool.getMax(nums) = " + ArraysTool.getMax(nums));
System.out.println("ArraysTool.getMin(nums) = " + ArraysTool.getMin(nums));
ArraysTool.reverse(nums);
ArraysTool.traversal(nums);
System.out.println("ArraysTool.findFirst(nums,2) = " + ArraysTool.findFirst(nums,2));
System.out.println("ArraysTool.findLast(nums,2) = " + ArraysTool.findLast(nums, 2));
}
}
class ArraysTool{
static void traversal(int[] nums){
System.out.print("[");
for (int n : nums) {
System.out.print(n + ",");
}
System.out.println("\b]");
}
static int getMax(int[] nums){
int res = Integer.MIN_VALUE;
for(int i = 0;i < nums.length;i++){
res = Math.max(res,nums[i]);
}
return res;
}
static int getMin(int[] nums){
int res = Integer.MAX_VALUE;
for(int i = 0;i < nums.length;i++){
res = Math.min(res,nums[i]);
}
return res;
}
static void reverse(int[] nums){
int len = nums.length;
for (int i = 0; i < len / 2; i++) {
int tmp = nums[i];
nums[i] = nums[len - 1 - i];
nums[len - 1 - i] = tmp;
}
}
static int findFirst(int[] nums,int target){
for (int i = 0; i < nums.length; i++) {
if(nums[i] == target){
return i;
}
}
return -1;
}
static int findLast(int[] nums,int target){
for (int i = nums.length - 1; i >= 0;i--) {
if(nums[i] == target){
return i;
}
}
return -1;
}
}导包相关练习 ​
导包是很基本的操作,某些细节也需要注意,请完成以下练习:
// 在com.cskaoyan.a包下,定义一个名为MyClazz的类如下
public class MyClazz {
public void hello() {
System.out.println("a包");
}
}
// 同时,在com.cskaoyan.b包下,一个类名也为MyClazz
public class MyClazz {
public void hello() {
System.out.println("b包");
}
}
// 同时在com.cskaoyan.b包下定义一个Test类如下:
public class Test {
public static void main(String[] args) {
MyClazz myClazz = new MyClazz();
myClazz.hello();
}
}首先根据上述注释,将三个类定义出来
毫无疑问,直接执行Test中的main方法会输出的是b包,思考以下问题:
不改变Test类中main方法的基础上,让main方法运行之后输出“a包”,应该怎么做?
导包
不做任何导包操作的基础上,修改main方法,让main方法运行之后同时输出“a包”和“b包”,应该怎么做?
在Test类中添加导包语句import com.cskaoyan.a.*,不修改任何代码,执行main方法,输出的是什么?为什么?
工具类禁止创建对象 ​
普遍来说,工具类不应该创建对象,应该私有化构造方法
需求一:在昨天自己的作业,数组工具类的基础上,私有化它的构造方法
需求二:
定义一个Scanner工具类ScannerUtils,提供以下方法:
1,键盘录入字符串
2,键盘录入int整数
3,键盘录入一个Person对象(Person类中有age和name属性)
不要忘记私有化构造器注:录入对象,无非就是先录入成员变量取值,然后构造器创建对象。
public class Main {
public static void main(String[] args) {
System.out.println(ScannerUtils.inputeStr());
System.out.println(ScannerUtils.inputeInt());
Person p = ScannerUtils.inputePerson();
System.out.println(p.toString());
}
}
class ScannerUtils{
private ScannerUtils(){
}
static Scanner sc = new Scanner(System.in);
static String inputeStr(){
return sc.nextLine();
}
static int inputeInt(){
return sc.nextInt();
}
static Person inputePerson(){
System.out.println("分别输入Person对象的名字和年龄:");
String name = sc.nextLine();
int age = sc.nextInt();
return new Person(name, age);
}
}
class Person{
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}禁止创建对象 ​
综合访问权限修饰符,static等知识点,做一个综合的练习。
定义一个Student类,并要求在其他类中,最多只能创建10个Student类的对象。
分析:
1,如果允许外部直接创建对象,显然无法控制创建对象的个数
2,需要计数器指示外部创建对象的个数class Student{
static int countObj = 0;
private Student(){
countObj++;
}
public static Student createObj(){
if(countObj >= 10){
return null;
}
return new Student();
}
}设计一个员工类 ​
设计一个员工类(Staff),要求该类: 属性: 姓名,性别,工号,工资 构造器: 姓名的单参构造器、姓名和性别的双参构造器、姓名、性别和工资的三参构造器 其中员工的工号要求(每一位员工都必须有工号): 第一个员工对象的工号是10001 第二个员工对象的工号是10002 第三个员工对象的工号是10003 .... 现在每位员工都要缴纳一个固定额的公积金(比如2000),要求在员工类中设计一个方法求员工的到手工资。(除了扣除这个固定额度的公积金,不考虑诸如扣税等其余问题) 最后再定义一个方法用于打印员工对象的各种属性取值,其中在打印工资时,要打印实际到手工资。 将该类定义完毕后,尝试创建对象,测试一下代码。
public class Test {
public static void main(String[] args) {
Staff stf1 = new Staff("小红");
Staff stf2 = new Staff("小白","男");
Staff stf3 = new Staff("小黑","男",10000);
stf1.print();
stf2.print();
stf3.print();
}
}
class Staff{
String name;
String gender;
int id;
double salary;
static int defaultId = 1000;
static final double FUND = 2000;
{
id = ++defaultId;
}
public Staff(String name){
this.name = name;
}
public Staff(String name, String gender) {
this.name = name;
this.gender = gender;
}
public Staff(String name, String gender, double salary) {
this(name, gender);
this.salary = salary;
}
public double getHomeSalary(){
return 0.0;
}
public void print() {
System.out.println("Staff{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", id=" + id +
", salary=" + (salary - FUND) +
'}');
}
}OOP2——封装 ​
主要针对OOP2阶段的:
面向对象第一个特征——封装
Getter/Setter方法 ​
当属性私有化,又有外界访问需求时,提供Getter/Setter方法
创建两个类,分别用来表示长方形和正方形。
同时定义所需的成员变量(边长),代表长方形或者正方形的边长(私有化成员变量,并提供相应的Getter/Setter方法,获取以及改变长方形和正方形的边长。)
然后在两个类中分别定义两个成员方法,用于求对应图形的面积和周长。
最后,写代码测试一下创建对象,方法调用等。注:Getter/Setter方法可以选择手写一个,其余的用快捷键自动生成。
class Square{
private double side;
public double perimeter(){
return 4 * getSide();
}
public double area(){
return Math.pow(getSide(), 2);
}
public double getSide() {
return side;
}
public void setSide(double side) {
this.side = side;
}
}
class Rectangle{
private double length;
private double width;
public double perimeter(){
return 2 * (getWidth() + getLength());
}
public double area(){
return getLength() * getWidth();
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
}OOP2——继承 ​
主要针对OOP2阶段中面向对象第二个特征——继承
包含:
- 继承的基本概念
- 继承中两个类的关系
- protected
- 子类对象初始化
- 方法覆盖
- final
- ....
继承基础语法练习 ​
明确记住,继承中的两个类应该有is-a关系
提供以下两个动物需要描述,请用你的知识来编写代码
猫:姓名,年龄,颜色,可以叫,可以抓老鼠
狗:姓名,年龄,性别,可以叫,可以看门
分析这个案例,设计出合适的继承体系。
最后思考:人类研究出来了机器人,它们也有姓名,年龄,颜色等属性,可以套用本题中的继承体系吗?class Animal {
protected String name;
protected int 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;
}
protected void sound() {
}
}
class Cat extends Animal {
private String color;
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Cat(String n, int a, String color) {
this.name = n;
this.age = a;
this.color = color;
}
public void sound() {
System.out.println("喵喵喵");
}
public void mice() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
String gender;
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Dog(String n, int a, String g) {
this.name = n;
this.age = a;
this.gender = g;
}
public void sound() {
System.out.println("汪汪汪");
}
public void watch() {
System.out.println("看门");
}
}练习继承中的构造器使用 ​
使用alt + insert快捷键可以快速生成各种类中结构。子类继承父类后,在子类中按alt + insert快捷键快速创建构造方法时,第一步会让你选择父类构造器(也就是super(参数))
随后才是选择子类自身的成员变量,生成子类构造器。
当然如果选择使用父类无参构造器,这时
super()是隐藏的,即子类对象隐式初始化。当选择使用子类有参构造器时,这时
super(参数)必须写在子类构造器第一行,这是子类对象的显式初始化。
给出三个层级的继承关系
顶层父类Grandfather,Father继承Grandfather,Son继承Father
成员变量:
Grandfather:gA,a
Father:fA,a
Son:sA,a
成员方法:
Grandfather:testGrand,test
Father:testFather,test
Son:testSon,test
使用快捷键在Son中生成不同的构造方法,调用不同的父类构造器(需要先生成父类相应的构造器)
随后使用多种方式创建Son对象(思考有几种),测试对象名访问成员的机制,理解属性隐藏和方法覆盖public class test2 {
public static void main(String[] args) {
Son s = new Son();
System.out.println(s.a); //2
Father f = new Son();
System.out.println(f.a); //1
GrandFather gf = new Son();
System.out.println(gf.a); //0
s.test();
f.test();
gf.test();
// f.testSon();
// gf.testFather();
}
}
class GrandFather{
int gA;
int a;
public void testGrand() {
System.out.println("testGrand");
}
public void test() {
System.out.println("GrandFather.test");
}
public GrandFather(int gA, int a) {
this.gA = gA;
this.a = a;
}
public GrandFather() {
}
}
class Father extends GrandFather{
int fA;
int a = 1;
public void testFather() {
System.out.println("testFather");
}
public void test() {
System.out.println("Father.test");
}
public Father(int fA, int a) {
this.fA = fA;
this.a = a;
}
public Father() {
}
public Father(int gA, int a, int fA, int a1) {
super(gA, a);
this.fA = fA;
this.a = a1;
}
}
class Son extends Father{
int sA;
int a = 2;
public void testSon() {
System.out.println("testSon");
}
public void test() {
System.out.println("Son.test");
}
public Son(int gA, int a, int fA, int a1, int sA, int a2) {
super(gA, a, fA, a1);
this.sA = sA;
this.a = a2;
}
public Son(int sA, int a) {
this.sA = sA;
this.a = a;
}
public Son(int gA, int a, int fA, int a1) {
super(gA, a, fA, a1);
}
public Son() {
}
}final语法练习 ​
final修饰成员/静态成员变量,表示一个常量,必须明确地赋值。
自定义一个类,类中定义三个成员变量a,b,c,用final修饰这三个成员变量
再定义两个静态成员变量staticA和staticB,也用final修饰这两个静态成员变量
然后:
1,请用三种不同的方式,分别为成员变量a,b,c赋值
2,请用两种不同的方式,分别为静态成员变量staticA和staticB赋值class Test{
final int a = 1;
final int b;
final int c;
{c = 3;}//构造代码块
static final int A = 4;
static final int B;
static{B = 5;}
public Test(int bb) {
b = bb;
}
}多态语法基础练习 ​
牢记多态发生的条件
请根据题目,作出合理设计,定义如下类:
父类Person
属性:String name,int age
行为:eat();
子类SouthPerson
属性:String name,int age,double salary
行为:eat(),swim()
子类NorthPerson
属性:String name,int age,double height
行为:eat(),drink()
写代码实现,eat()方法的多态效果
1,人都要吃饭
2,南方人喜欢吃米饭
3,北方人喜欢吃面食
最后,在测试类中,编写测试代码,要求进行如下测试:
1,编写测试方法,
要求该方法允许传入SouthPerson对象和NorthPerson对象,并在方法体中调用它们的eat()方法
方法调用的结果一致吗?
2,用父类引用指向子类对象的方式创建SouthPerson对象,能否直接访问salary属性和swim()方法?
如果不能,应该怎么写代码让它能够正常调用?
3,用父类引用指向子类对象的方式创建NorthPerson对象,能否(直接或写代码)访问salary属性和swim()方法?
如果不能,将该对象引用强转为SouthPerson引用,能否成功?为什么?public class ExerciseBlock {
public static void main(String[] args) {
Person p = new Person();
p.eat();
Person sp = new SouthPerson();
sp.eat();
Person np = new NorthPerson();
np.eat();
personEat(p);
personEat(sp);
personEat(np);
((SouthPerson) sp).swim();
double d = ((SouthPerson) sp).salary;
if(np instanceof SouthPerson){
double salary = ((SouthPerson) np).salary;
((SouthPerson) np).swim();
}else System.out.println("np不能强转为SouthPerson");
}
public static void personEat(Person ps){
ps.eat();
}
}
class Person{
String name;
int age;
public void eat() {
System.out.println("人都要吃饭");
}
}
class SouthPerson extends Person{
double salary;
public void eat(){
System.out.println("南方人爱吃米饭");
}
public void swim(){
System.out.println("南方人会游泳");
}
}
class NorthPerson extends Person{
double height;
public void eat(){
System.out.println("北方人爱吃面食");
}
public void drink(){
System.out.println("北方人爱喝酒");
}
}OOP3——抽象类和接口 ​
主要针对OOP3阶段:
包含:
- 抽象类
- 接口
抽象类基础语法练习 ​
完成抽象类的基础语法练习,按照说明操作即可。
定义抽象类A,抽象类B继承A,普通类C继承B。
A类中,定义成员变量a赋值为10,抽象showA方法
B类中,定义成员变量b赋值为20,抽象showB方法
C类中,定义成员变量c赋值为30,重写showA方法打印a,重写showB方法打印b,定义showC方法,打印c
然后在测试类中,创建C类的对象,调用showA方法,showB方法,showC方法。
然后查看方法调用结果,思考为什么会出现这种现象。public class Test {
public static void main(String[] args) {
C c = new C();
c.showA();
c.showB();
c.showC();
}
}
abstract class A {
int a = 10;
abstract void showA();
}
abstract class B extends A {
int b = 20;
abstract void showB();
}
class C extends B {
int c = 30;
@Override
void showA() {
System.out.println(a);
}
@Override
void showB() {
System.out.println(b);
}
void showC() {
System.out.println(c);
}
}接口与抽象类基础语法练习 ​
从实际角度出发,接口和抽象类的差异是十分明显的。可以参考完成以下案例:
学生和老师都有共同的属性: name、gender、age
共同的行为:eat() sleep()
注:虽然行为一致,但实现会不同。
现在为了提升自身素质,大家都需要额外进行技能的学习:学生需要增强实践动手能力,老师需要增强语言能力。
请定义抽象类和接口,描述以上体系。
然后用以下方式进行测试:
1,用不同的父类指向不同的子类对象,理解方法调用时“编译时看左边”
2,用这些引用调用方法,理解方法调用时“运行时看右边”public class Test {
public static void main(String[] args) {
Study stu = new Teacher();
Study sty = new Student();
stu.learnEnglish();
sty.learnEnglish();
}
}
abstract class Person{
String name;
String gender;
int age;
abstract public void eat();
abstract public void sleep();
}
interface Study{
public void learnEnglish();
}
class Student extends Person implements Study{
@Override
public void learnEnglish() {
System.out.println("学生增强动手实践");
}
@Override
public void eat() {
System.out.println("学生吃饭");
}
@Override
public void sleep() {
System.out.println("学生睡觉");
}
}
class Teacher extends Person implements Study{
@Override
public void learnEnglish() {
System.out.println("老师增强语言能力");
}
@Override
public void eat() {
System.out.println("老师吃饭");
}
@Override
public void sleep() {
System.out.println("老师睡觉");
}
}OOP3——内部类 ​
主要针对OOP3阶段:
包含:
- 成员内部类、静态内部类、局部内部类
- 匿名内部类、Lambda表达式
填代码题 ​
成员内部类,静态内部类的区别实际上时非常明显的,对象的创建和使用都有很大差异。请完成下列题目:
根据注释填写(1),(2),(3)处的代码:
public class Test{ public static void main(String[] args){ //(1)创建并初始化Bean1类对象bean1 bean1.i++; //(2)创建并初始化Bean2类对象bean2 bean2.j++; //(3)创建并初始化Bean3类对象bean3 bean3.k++; } class Bean1{ public int i = 0; } static class Bean2{ public int j = 0; } } class Bean{ class Bean3{ public int k = 0; } }
public class Test{
public static void main(String[] args){
//(1)创建并初始化Bean1类对象bean1
Bean1 bean1 = new Test().new Bean1();
bean1.i++;
//(2)创建并初始化Bean2类对象bean2
Bean2 bean2 = new Bean2();
bean2.j++;
//(3)创建并初始化Bean3类对象bean3
Bean.Bean3 bean3 = new Bean().new Bean3();
bean3.k++;
}
class Bean1{
public int i = 0;
}
static class Bean2{
public int j = 0;
}
}
class Bean{
class Bean3{
public int k = 0;
}
}用内部类来实现接口 ​
定义一个接口Compute,用来完成计算器的功能,比如最简单的加减乘除功能。
请用以下两种方式测试:
1,编写实现类进行测试
2,用局部内部类进行测试
3,使用匿名内部类进行测试public class Test{
public static void main(String[] args){
ComputeImpl com = new ComputeImpl();
System.out.println("add(1,2) = " + com.add(1, 2));
//局部内部类
class InnerComp implements Compute{
@Override
public int add(int op1, int op2) {
return op1 + op2;
}
@Override
public int sub(int op1, int op2) {
return op1 - op2;
}
@Override
public int mul(int op1, int op2) {
return op1 * op2;
}
@Override
public int div(int op1, int op2) {
if(op2 == 0) return 0;
return op1 / op2;
}
}
System.out.println("sub(4,3) = " + new InnerComp().sub(4,3));
//匿名内部类
int res = new Compute() {
@Override
public int add(int op1, int op2) {
return op1 + op2;
}
@Override
public int sub(int op1, int op2) {
return op1 - op2;
}
@Override
public int mul(int op1, int op2) {
return op1 * op2;
}
@Override
public int div(int op1, int op2) {
if (op2 == 0) return 0;
return op1 / op2;
}
}.mul(4, 5);
System.out.println("mul(4,5) = " + res);
}
}
//实现类
class ComputeImpl implements Compute{
@Override
public int add(int op1, int op2) {
return op1 + op2;
}
@Override
public int sub(int op1, int op2) {
return op1 - op2;
}
@Override
public int mul(int op1, int op2) {
return op1 * op2;
}
@Override
public int div(int op1, int op2) {
if(op2 == 0) return 0;
return op1 / op2;
}
}
interface Compute{
int add(int op1, int op2);
int sub(int op1, int op2);
int mul(int op1, int op2);
int div(int op1, int op2);
}成员内部类练习 ​
成员内部类对象依赖于它的外围类对象
定义一个类Dog
属性:age,name
除此之外,Dog类中需要定义一个成员内部类Body,Body类中有属性color
请私有化该成员内部类,然后将该成员内部类对象作为外围类的成员变量私有化。
然后在Dog类提供一个方法,展示Dog类的全部属性(包括成员内部类中的)。
最后在一个外部类中,创建Dog对象,让外界感知不到成员内部类的存在。
思考一下如何做这个需求?成员内部类练习 ​
成员内部类题目:
定义一个外部类Car,在外部类中定义一个成员内部类Engine(发动机)。
Car类包含私有成员变量brand,price,model等。
然后提供构造器给成员变量赋值,以及一个成员方法startEngine(),
该方法创建一个Engine对象并调用其run()方法表示启动发动机。
成员内部类Engine当中提供一个run成员方法,要打印汽车的信息,并提示发动机已启动。public class Test{
public static void main(String[] args) {
Car car = new Car("奔驰",100000,"e300");
car.startEngine();
}
}
class Car{
private String brand;
private double price;
private String model;
public Car(String brand, double price, String model) {
this.brand = brand;
this.price = price;
this.model = model;
}
void startEngine(){
new Engine().run();
}
class Engine{
void run(){
System.out.println("品牌:" + brand + " 价格:" + price
+ " 型号:" + model + " 的汽车发动机启动");
}
}
}静态内部类练习 ​
创建一个名为Library的外部类,其中包含一个名为Book的静态内部类。
Book类包含两个私有成员变量:String title和String author,
以及一个构造函数用于初始化这些变量。
Library类存在一个Book对象数组作为成员变量,用于存储Book对象。
再定义一个成员方法addBook,该方法需要传入书名和作者作为参数,
然后将这个Book对象添加到成员变量的Book数组中
(找null的位置,进行插入,可以考虑将Book数组的长度初始值给大一点)
Library类包含一个名为displayBooks的方法,用于打印Book对象数组中所有书籍的详细信息。
Book类还包含一个名为displayInfo的方法,用于打印书籍的详细信息。
最后,在测试类当中创建Library对象并调用方法,进行代码测试。public class Test{
public static void main(String[] args) {
Library lib = new Library();
lib.addBook("红楼梦", "曹雪芹");
lib.addBook("西游记", "吴承恩");
lib.addBook("水浒传", "施耐庵");
lib.addBook("三国演义", "罗贯中");
lib.displayBooks();
}
}
class Library{
static class Book{
private String title;
private String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
@Override
public String toString() {
return "Book{" +
"title='" + title + '\'' +
", author='" + author + '\'' +
'}';
}
}
private Book[] books = new Book[10];
boolean addBook(String t, String a) {
Book newBook = new Book(t, a);
for (int i = 0; i < books.length; i++) {
if (books[i] == null) {
books[i] = newBook;
return true;
}
}
return false;
}
void displayBooks(){
for (Book b : books) {
if(b == null) continue;
System.out.println(b.toString());
}
}
}lambda表达式的练习 ​
lambda表达式的书写,除了注意格式外,最重要的是关注类型推断
- 提供以下6个功能接口,请用lambda表达式分别创建对象,调用test()方法
- 自由发挥lambda表达式的书写
//无返回值无参数的功能接口
@FunctionalInterface
interface INoReturnNoParam {
void test();
}
//无返回值有一个参数的功能接口
@FunctionalInterface
interface INoReturnOneParam {
void test(int a);
}
//无返回值两个参数的功能接口
@FunctionalInterface
interface INoReturnTwoParam {
void test(int a, int b);
}
//有返回值无参数的功能接口
@FunctionalInterface
interface IHasReturnNoParam {
int test();
}
//有返回值一个参数的功能接口
@FunctionalInterface
interface IHasReturnOneParam {
int method(int a);
}
//有返回值两个参数的功能接口
@FunctionalInterface
interface IHasReturnTwoParam {
int test(int a, int b);
}public class Demo {
public static void main(String[] args) {
INoReturnNoParam nrnp = () -> System.out.println("NoReturnNoParam");
nrnp.test();
INoReturnOneParam nrop = a -> System.out.println("INoReturnOneParam " + a);
nrop.test(3);
INoReturnTwoParam nrtp = (a, b) -> System.out.println("INoReturnTwoParam " + (a + b));
nrtp.test(50, 2);
//IHasReturnNoParam hrnp = () -> { return 2; };
IHasReturnNoParam hrnp = () -> 2;
System.out.println("hrnp.test() = " + hrnp.test());
IHasReturnOneParam hrop = (a) -> a;
System.out.println("hrop.method(1) = " + hrop.method(1));
IHasReturnTwoParam hrtp = (a, b) -> a + b;
System.out.println("hrtp.test(2, 4) = " + hrtp.test(2, 4));
}
}
//无返回值无参数的功能接口
@FunctionalInterface
interface INoReturnNoParam {
void test();
}
//无返回值有一个参数的功能接口
//功能接口并不是只能由一个抽象方法
@FunctionalInterface
interface INoReturnOneParam {
void test(int a);
boolean equals(Object o);
}
//无返回值两个参数的功能接口
@FunctionalInterface
interface INoReturnTwoParam {
void test(int a, int b);
}
//有返回值无参数的功能接口
@FunctionalInterface
interface IHasReturnNoParam {
int test();
}
//有返回值一个参数的功能接口
@FunctionalInterface
interface IHasReturnOneParam {
int method(int a);
}
//有返回值两个参数的功能接口
@FunctionalInterface
interface IHasReturnTwoParam {
int test(int a, int b);
}今天先学会使用匿名内部类和lambda表达式,具体详细的有哪些用途,后面会碰到
- 如果实在感兴趣想知道,你也可以自己搜一搜,Java对象数组的排序
Lambda表达式的练习 ​
定义一个计算(compute)接口,接口中有加减乘除四个抽象方法。
然后使用匿名内部类去实现加减乘除并测试上述功能,昨天已经实现了,今天考虑用lambda改进它,如下:
- 用以下功能接口,实现一个计算器的工具类
- 在工具类中,只需要一个工具方法,就能够实现所有的计算功能
@FunctionalInterface
interface Compute {
double compute(double a, double b);
}工具类和工具方法如下:
//需要提供一个使用功能接口的方法完成需求
class ComputeTool {
private ComputeTool() {
}
public static void calc(Compute com, double a, double b) {
//...
}
}你知道如何使用吗?联系一下,今天讲解的String数组的过滤,
public class Demo {
public static void main(String[] args) {
System.out.println(ComputeTool.calc((op1, op2) -> op1 + op2, 90, 32));
System.out.println(ComputeTool.calc((op1, op2) -> op1 - op2, 90, 32));
System.out.println(ComputeTool.calc((op1, op2) -> op1 * op2, 90, 32));
System.out.println(ComputeTool.calc((op1, op2) -> op1 / op2, 90, 32));
}
}
@FunctionalInterface
interface Compute{
double calc(double op1, double op2);
}
class ComputeTool {
private ComputeTool() {
}
public static double calc(Compute com, double a, double b) {
return com.calc(a, b);
}
}Object类 ​
主要包含内容是Object类的成员方法:
- getClass()
- toString()
- equals()
- hashCode()
- finalize()
- clone()
getClass()方法练习 ​
定义两个类,然后分别创建对象,调用getClass方法
用“==”号比较它们的运行时Class对象是否相等,并说明原因
理解运行时类对象、类加载、类的对象的区别
public class Demo {
public static void main(String[] args) {
A a = new A();
A a1 = new A();
B b = new B();
Class aClass = a.getClass();
Class aClass1 = a1.getClass();
Class bClass = b.getClass();
System.out.println(aClass==aClass1);
System.out.println(aClass==bClass);
System.out.println(classEqual(a, b));
}
public static boolean classEqual(Object a,Object b){
return (a.getClass()==b.getClass());
}
}
class A{
}
class B{
}equals方法练习 ​
定义一个Animal类
成员变量:
int age,String name,double price
手写它的equals方法,比较getClass和instanceof的区别public class Demo {
public static void main(String[] args) {
Animal a = new Animal(3, "二哈", 99.8);
Animal a1 = new Animal(3, "二哈", 99.8);
System.out.println(a1.equals(a1));
//Animal2中重写equals方法用了instanceof取代getClass
Animal2 a2 = new Animal2(3, "二哈", 99.8);
Dog dog = new Dog(3, "二哈", 99.8);
System.out.println(a2.equals(dog));
}
}
class Animal{
private int age;
private String name;
private double price;
public Animal(int age, String name, double price) {
this.age = age;
this.name = name;
this.price = price;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Animal animal = (Animal) o;
return age == animal.age &&
Double.compare(animal.price, price) == 0 &&
Objects.equals(name, animal.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name, price);
}
}
class Animal2{
private int age;
private String name;
private double price;
public Animal2(int age, String name, double price) {
this.age = age;
this.name = name;
this.price = price;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof Animal2)) return false;
Animal2 animal2 = (Animal2) o;
return age == animal2.age &&
Double.compare(animal2.price, price) == 0 &&
Objects.equals(name, animal2.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name, price);
}
}
class Dog extends Animal2{
public Dog(int age, String name, double price) {
super(age, name, price);
}
}深度克隆练习 ​
String虽然也是引用数据类型,但无需考虑它的深度克隆。
分别定义以下类:
教师类Teacher
属性:int age;String name;Student stu
学生类Student
属性:int age;String name;Star s
明星类Star
属性:int age,String name
尝试完成Teacher对象的深度克隆,并写代码进行测试public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
Teacher t1 = new Teacher(28, "王老师",
new Student(14, "小明", new Star(36, "刘亦菲")));
Teacher clone = t1.clone();
System.out.println(clone.name.equals(t1.name));
System.out.println(clone.stu == t1.stu);//浅克隆true,深克隆false
System.out.println(clone.stu.s == t1.stu.s);//浅克隆true,深克隆false
t1.stu.s.name = "张靓颖";
System.out.println(clone.stu.s);
System.out.println(t1.stu.s);
}
}
class Teacher implements Cloneable{
int age;
String name;
Student stu;
@Override
public Teacher clone() throws CloneNotSupportedException {
Teacher ct = (Teacher) super.clone();
ct.stu = ct.stu.clone();
return ct;
}
public Teacher(int age, String name, Student stu) {
this.age = age;
this.name = name;
this.stu = stu;
}
}
class Student implements Cloneable{
int age;
String name;
Star s;
public Student(int age, String name, Star s) {
this.age = age;
this.name = name;
this.s = s;
}
@Override
public Student clone() throws CloneNotSupportedException {
Student cs = (Student) super.clone();
cs.s = cs.s.clone();
return cs;
}
}
class Star implements Cloneable{
int age;
String name;
public Star(int age, String name) {
this.age = age;
this.name = name;
}
@Override
protected Star clone() throws CloneNotSupportedException {
return (Star) super.clone();
}
@Override
public String toString() {
return "Star{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}