数据类型 类型 Java语言数据类型的分类
基本数据类型的四类八种
数据类型
关键字
内存占用
取值范围
整数
byte
1
负的2的7次方 ~ 2的7次方-1(-128~127)
short
2
负的2的15次方 ~ 2的15次方-1(-32768~32767)
int
4
负的2的31次方 ~ 2的31次方-1
long
8
负的2的63次方 ~ 2的63次方-1
浮点数
float
4
1.401298e-45 ~ 3.402823e+38
double
8
4.9000000e-324 ~ 1.797693e+308
字符
char
2
0-65535
布尔
boolean
1
true,false
整数类型和小数类型的取值范围大小关系:
double > float > long > int > short > byte
键盘录入 1 2 3 4 5 6 7 8 9 10 11 12 13 import java.util.Scanner;public class ScannerDemo1 { public static void main (String[] args) { Scanner sc = new Scanner (System.in); System.out.println("请输入一个数字" ); int i = sc.nextInt(); System.out.println(i); } }
隐式转换 概念: 也叫自动类型提升 ,就是把一个取值范围小的数据或者变量,赋值给另一个取值范围大的变量。此时不需要我们额外写代码单独实现,是程序自动帮我们完成的 。
两种提升规则:
取值范围小的,和取值范围大的进行运算,小的会先提升为大的,再进行运算。
byte、short、char三种类型的数据在运算的时候,都会直接先提升为int,然后再进行运算。
取值范围从小到大的关系
byte short int long float double
强制转换 概念: 如果要把一个取值范围大的数据或者变量赋值给另一个取值范围小的变量。是不允许直接操作。
书写格式: 目标数据类型 变量名 = (目标数据类型)被强转的数据;
switch的扩展知识:
default的位置和省略情况
default可以放在任意位置,也可以省略
case穿透
不写break会引发case穿透现象
switch在JDK12的新特性
1 2 3 4 5 6 7 int number = 10 ;switch (number) { case 1 -> System.out.println("一" ); case 2 -> System.out.println("二" ); case 3 -> System.out.println("三" ); default -> System.out.println("其他" ); }
当我们需要对一个范围进行判断的时候,用if的第三种格式
当我们把有限个数据列举出来,选择其中一个执行的时候,用switch语句
比如:
小明的考试成绩,如果用switch,那么需要写100个case,太麻烦了,所以用if简单。
如果是星期,月份,客服电话中0~9的功能选择就可以用switch
练习:需求:键盘录入星期数,输出工作日、休息日。 (1-5) 工作日,(6-7)休息日。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 Scanner sc = new Scanner (System.in);System.out.println("请输入星期" ); int week = sc.nextInt();---------------------------------------------------- 利用case 穿透简化代码 switch (week){ case 1 : case 2 : case 3 : case 4 : case 5 : System.out.println("工作日" ); break ; case 6 : case 7 : System.out.println("休息日" ); break ; default : System.out.println("没有这个星期" ); break ; } ---------------------------------------------------- 利用JDK12简化代码书写 switch (week) { case 1 , 2 , 3 , 4 , 5 -> System.out.println("工作日" ); case 6 , 7 -> System.out.println("休息日" ); default -> System.out.println("没有这个星期" ); }
练习:珠穆朗玛峰 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int height = 8844430 ;double paper = 0.1 ;int count = 0 ;while (paper < height){ paper = paper * 2 ; count++; } System.out.println(count);
练习:统计个数 需求:生成10个1~100之间的随机数存入数组。
求出所有数据的和
求所有数据的平均数
统计有多少个数据比平均值小
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 int [] arr = new int [10 ];Random r = new Random ();for (int i = 0 ; i < arr.length; i++) { int number = r.nextInt(100 ) + 1 ; arr[i] = number; } int sum = 0 ;for (int i = 0 ; i < arr.length; i++) { sum = sum + arr[i]; } System.out.println("数组中所有数据的和为:" + sum); int avg = sum / arr.length;System.out.println("数组中平均数为:" + avg); int count = 0 ;for (int i = 0 ; i < arr.length; i++) { if (arr[i] < avg){ count++; } } System.out.println("在数组中,一共有" + count + "个数据,比平均数小" ); for (int i = 0 ; i < arr.length; i++) { System.out.print(arr[i] + " " ); }
方法 方法
方法(method)是程序中最小的执行单元
注意:
方法必须先创建才可以使用,该过程成为方法定义
方法创建后并不是直接可以运行的,需要手动使用后,才执行,该过程成为方法调用
方法重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class MethodTest { public static void main (String[] args) { System.out.println(compare(10 , 20 )); System.out.println(compare((byte ) 10 , (byte ) 20 )); System.out.println(compare((short ) 10 , (short ) 20 )); System.out.println(compare(10L , 20L )); } public static boolean compare (int a, int b) { System.out.println("int" ); return a == b; } public static boolean compare (byte a, byte b) { System.out.println("byte" ); return a == b; } public static boolean compare (short a, short b) { System.out.println("short" ); return a == b; } public static boolean compare (long a, long b) { System.out.println("long" ); return a == b; } }
综合练习 练习一:飞机票 需求:
机票价格按照淡季旺季、头等舱和经济舱收费、输入机票原价、月份和头等舱或经济舱。
按照如下规则计算机票价格:旺季(5-10月)头等舱9折,经济舱8.5折,淡季(11月到来年4月)头等舱7折,经济舱6.5折。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 import java.util.Scanner;public class Test1 { public static void main (String[] args) { Scanner sc = new Scanner (System.in); System.out.println("请输入机票的原价" ); int ticket = sc.nextInt(); System.out.println("请输入当前的月份" ); int month = sc.nextInt(); System.out.println("请输入当前购买的舱位 0 头等舱 1 经济舱" ); int seat = sc.nextInt(); if (month >= 5 && month <= 10 ) { ticket = getTicket(ticket, seat, 0.9 , 0.85 ); } else if ((month >= 1 && month <= 4 ) || (month >= 11 && month <= 12 )) { ticket = getTicket(ticket, seat, 0.7 , 0.65 ); } else { System.out.println("键盘录入的月份不合法" ); } System.out.println(ticket); } public static int getTicket (int ticket, int seat, double v, double v2) { if (seat == 0 ) { ticket = (int ) (ticket * v); } else if (seat == 1 ) { ticket = (int ) (ticket * v2); } else { System.out.println("没有这个舱位" ); } return ticket; } }
练习二:打印素数 判断101~200之间有多少个素数,并输出所有素数。
备注:素数就是质数
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class Test2 { public static void main (String[] args) { int count = 0 ; for (int i = 101 ; i <= 200 ; i++) { boolean flag = true ; for (int j = 2 ; j < i; j++) { if (i % j == 0 ){ flag = false ; break ; } } if (flag){ System.out.println("当前数字" +i+"是质数" ); count++; } } System.out.println("一共有" + count + "个质数" ); } }
练习三:验证码 需求:
定义方法实现随机产生一个5位的验证码
验证码格式:
长度为5
前四位是大写字母或者小写字母
最后一位是数字
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 import java.util.Random;public class Test3 { public static void main (String[] args) { char [] chs = new char [52 ]; for (int i = 0 ; i < chs.length; i++) { if (i <= 25 ){ chs[i] = (char )(97 + i); }else { chs[i] = (char )(65 + i - 26 ); } } String result = "" ; Random r = new Random (); for (int i = 0 ; i < 4 ; i++) { int randomIndex = r.nextInt(chs.length); result = result + chs[randomIndex]; } int number = r.nextInt(10 ); result = result + number; System.out.println(result); } }
练习四:复制数组 需求:
把一个数组中的元素复制到另一个新数组中去。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class Test4 { public static void main (String[] args) { int [] arr = {1 ,2 ,3 ,4 ,5 }; int [] newArr = new int [arr.length]; for (int i = 0 ; i < arr.length; i++) { newArr[i] = arr[i]; } for (int i = 0 ; i < newArr.length; i++) { System.out.println(newArr[i]); } } }
练习五:评委打分 需求 :
在唱歌比赛中,有6名评委给选手打分,分数范围是[0 - 100]之间的整数。选手的最后得分为:去掉最高分、最低分后的4个评委的平均分,请完成上述过程并计算出选手的得分。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 package com.itheima.test;import java.util.Scanner;public class Test5 { public static void main (String[] args) { int [] scoreArr = getScores(); for (int i = 0 ; i < scoreArr.length; i++) { System.out.println(scoreArr[i]); } int max = getMax(scoreArr); int min = getMin(scoreArr); int sum = getSum(scoreArr); int avg = (sum - max - min)/(scoreArr.length - 2 ); System.out.println("选手的最终得分为:" + avg); } public static int getSum (int [] scoreArr) { int sum = 0 ; for (int i = 0 ; i < scoreArr.length; i++) { sum = sum + scoreArr[i]; } return sum; } public static int getMax (int [] scoreArr) { int max = scoreArr[0 ]; for (int i = 1 ; i < scoreArr.length; i++) { if (scoreArr[i] > max){ max = scoreArr[i]; } } return max; } public static int getMin (int [] scoreArr) { int min = scoreArr[0 ]; for (int i = 1 ; i < scoreArr.length; i++) { if (scoreArr[i] < min){ min = scoreArr[i]; } } return min; } public static int [] getScores(){ int [] scores = new int [6 ]; Scanner sc = new Scanner (System.in); for (int i = 0 ; i < scores.length; ) { System.out.println("请输入评委的打分" ); int score = sc.nextInt(); if (score >=0 && score<= 100 ){ scores[i] = score; i++; }else { System.out.println("成绩超出了范围,继续录入,当前的i为:" + i); } } return scores; } }
练习六:数字加密 需求:
某系统的数字密码(大于0),比如1983,采用加密方式进行传输。
规则如下:
先得到每位数,然后每位数都加上5 , 再对10求余,最后将所有数字反转,得到一串新数。
举例:
1 2 3 4 5 1 9 8 3 +5 6 14 13 8 %10 6 4 3 8 反转 8 3 4 6 加密后的结果就是:8346
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package com.itheima.test;public class Test6 { public static void main (String[] args) { int [] arr = {1 , 9 , 8 , 3 }; for (int i = 0 ; i < arr.length; i++) { arr[i] = arr[i] + 5 ; } for (int i = 0 ; i < arr.length; i++) { arr[i] = arr[i] % 10 ; } for (int i = 0 , j = arr.length - 1 ; i < j; i++, j--) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } int number = 0 ; for (int i = 0 ; i < arr.length; i++) { number = number * 10 + arr[i]; } System.out.println(number); } }
练习六扩展: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class Test7 { public static void main (String[] args) { int number = 12345 ; int temp = number; int count = 0 ; while (number != 0 ){ number = number / 10 ; count++; } int [] arr = new int [count]; int index = arr.length -1 ; while (temp != 0 ){ int ge = temp % 10 ; temp = temp / 10 ; arr[index] = ge; index--; } for (int i = 0 ; i < arr.length; i++) { System.out.print(arr[i] + " " ); } } }
练习七:数字解密 把上一题加密之后的数据进行解密
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public class Test8 { public static void main (String[] args) { int [] arr = {8 , 3 , 4 , 6 }; for (int i = 0 , j = arr.length - 1 ; i < j; i++, j--) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } for (int i = 0 ; i < arr.length; i++) { if (arr[i] >= 0 && arr[i] <= 4 ) { arr[i] = arr[i] + 10 ; } } for (int i = 0 ; i < arr.length; i++) { arr[i] = arr[i] - 5 ; } int number = 0 ; for (int i = 0 ; i < arr.length; i++) { number = number * 10 + arr[i]; } System.out.println(number); } }
练习八:抽奖 需求:
一个大V直播抽奖,奖品是现金红包,分别有{2, 588 , 888, 1000, 10000}五个奖金。请使用代码模拟抽奖,打印出每个奖项,奖项的出现顺序要随机且不重复。打印效果如下:(随机顺序,不一定是下面的顺序)
1 2 3 4 5 888 元的奖金被抽出588 元的奖金被抽出10000 元的奖金被抽出1000 元的奖金被抽出2 元的奖金被抽出
解法一: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package com.itheima.test;import java.util.Random;public class Test9 { public static void main (String[] args) { int [] arr = {2 , 588 , 888 , 1000 , 10000 }; int [] newArr = new int [arr.length]; Random r = new Random (); for (int i = 0 ; i < 5 ; ) { int randomIndex = r.nextInt(arr.length); int prize = arr[randomIndex]; boolean flag = contains(newArr, prize); if (!flag){ newArr[i] = prize; i++; } } for (int i = 0 ; i < newArr.length; i++) { System.out.println(newArr[i]); } } public static boolean contains (int [] arr,int prize) { for (int i = 0 ; i < arr.length; i++) { if (arr[i] == prize){ return true ; } } return false ; } }
解法二: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import java.util.Random;public class Test10 { public static void main (String[] args) { int [] arr = {2 , 588 , 888 , 1000 , 10000 }; Random r = new Random (); for (int i = 0 ; i < arr.length; i++) { int randomIndex = r.nextInt(arr.length); int temp = arr[i]; arr[i] = arr[randomIndex]; arr[randomIndex] = temp; } for (int i = 0 ; i < arr.length; i++) { System.out.println(arr[i]); } } }
练习九:双色球 代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 import java.util.Random;import java.util.Scanner;public class Test11 { public static void main (String[] args) { int [] arr = createNumber(); System.out.println("=======================" ); for (int i = 0 ; i < arr.length; i++) { System.out.print(arr[i] + " " ); } System.out.println("=======================" ); int [] userInputArr = userInputNumber(); int redCount = 0 ; int blueCount = 0 ; for (int i = 0 ; i < userInputArr.length - 1 ; i++) { int redNumber = userInputArr[i]; for (int j = 0 ; j < arr.length - 1 ; j++) { if (redNumber == arr[j]){ redCount++; break ; } } } int blueNumber = userInputArr[userInputArr.length-1 ]; if (blueNumber == arr[arr.length - 1 ]){ blueCount++; } if (redCount == 6 && blueCount == 1 ){ System.out.println("恭喜你,中奖1000万" ); }else if (redCount == 6 && blueCount == 0 ){ System.out.println("恭喜你,中奖500万" ); }else if (redCount == 5 && blueCount == 1 ){ System.out.println("恭喜你,中奖3000" ); }else if ((redCount == 5 && blueCount == 0 ) || (redCount == 4 && blueCount == 1 )){ System.out.println("恭喜你,中奖200" ); }else if ((redCount == 4 && blueCount == 0 ) || (redCount == 3 && blueCount == 1 )){ System.out.println("恭喜你,中奖10" ); }else if ((redCount == 2 && blueCount == 1 ) || (redCount == 1 && blueCount == 1 )|| (redCount == 0 && blueCount == 1 )){ System.out.println("恭喜你,中奖5" ); }else { System.out.println("谢谢参与,谢谢惠顾" ); } } public static int [] userInputNumber() { int [] arr = new int [7 ]; Scanner sc = new Scanner (System.in); for (int i = 0 ; i < 6 ; ) { System.out.println("请输入第" + (i + 1 ) + "个红球号码" ); int redNumber = sc.nextInt(); if (redNumber >= 1 && redNumber <= 33 ) { boolean flag = contains(arr, redNumber); if (!flag) { arr[i] = redNumber; i++; } else { System.out.println("当前红球号码已经存在,请重新输入" ); } } else { System.out.println("当前红球号码超出范围" ); } } System.out.println("请输入篮球号码" ); while (true ) { int blueNumber = sc.nextInt(); if (blueNumber >= 1 && blueNumber <= 16 ) { arr[arr.length - 1 ] = blueNumber; break ; } else { System.out.println("当前篮球号码超出范围" ); } } return arr; } public static int [] createNumber() { int [] arr = new int [7 ]; Random r = new Random (); for (int i = 0 ; i < 6 ; ) { int redNumber = r.nextInt(33 ) + 1 ; boolean flag = contains(arr, redNumber); if (!flag) { arr[i] = redNumber; i++; } } int blueNumber = r.nextInt(16 ) + 1 ; arr[arr.length - 1 ] = blueNumber; return arr; } public static boolean contains (int [] arr, int number) { for (int i = 0 ; i < arr.length; i++) { if (arr[i] == number) { return true ; } } return false ; } }
标准类制作 ① 类名需要见名知意
② 成员变量使用private修饰
③ 提供至少两个构造方法
④ get和set方法
提供每一个成员变量对应的setXxx()/getXxx()
⑤ 如果还有其他行为,也需要写上
API API概述
String类 String 类代表字符串,Java 程序中的所有字符串文字(例如“abc”)都被实现为此类的实例。也就是说,Java 程序中所有的双引号字符串,都是 String 类的对象。String 类在 java.lang 包下,所以使用的时候不需要导包!
字符串不可变,它们的值在创建后不能被更改
虽然 String 的值是不可变的,但是它们可以被共享
字符串效果上相当于字符数组( char[] ),但是底层原理是字节数组( byte[] )
String类的构造方法
常用的构造方法
方法名
说明
public String()
创建一个空白字符串对象,不含有任何内容
public String(char[] chs)
根据字符数组的内容,来创建字符串对象
public String(byte[] bys)
根据字节数组的内容,来创建字符串对象
String s = “abc”;
直接赋值的方式创建字符串对象,内容就是abc
创建字符串对象两种方式的区别
字符串的比较 ==号的作用
比较基本数据类型:比较的是具体的值
比较引用数据类型:比较的是对象地址值
equals方法的作用
方法介绍
1 public boolean equals (String s) 比较两个字符串内容是否相同、区分大小写
示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class StringDemo02 { public static void main (String[] args) { char [] chs = {'a' , 'b' , 'c' }; String s1 = new String (chs); String s2 = new String (chs); String s3 = "abc" ; String s4 = "abc" ; System.out.println(s1 == s2); System.out.println(s1 == s3); System.out.println(s3 == s4); System.out.println("--------" ); System.out.println(s1.equals(s2)); System.out.println(s1.equals(s3)); System.out.println(s3.equals(s4)); } }
字符串反转案例 案例需求 定义一个方法,实现字符串反转。键盘录入一个字符串,调用该方法后,在控制台输出结果
例如,键盘录入 abc,输出结果 cba
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class Test6 反转字符串 { public static void main (String[] args) { Scanner sc = new Scanner (System.in); System.out.println("请输入一个字符串" ); String str = sc.next(); String result = reverse(str); System.out.println(result); } public static String reverse (String str) { String s = "" ; for (int i = str.length() - 1 ; i >= 0 ; i--) { s = s + str.charAt(i); } return s; } }
金额转换 案例需求 把2135变成:零佰零拾零万贰仟壹佰叁拾伍元
把789变成:零佰零拾零万零仟柒佰捌拾玖元
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 package com.itheima.stringdemo;import java.util.Scanner;public class StringDemo9 { public static void main (String[] args) { Scanner sc = new Scanner (System.in); int money; while (true ) { System.out.println("请录入一个金额" ); money = sc.nextInt(); if (money >= 0 && money <= 9999999 ) { break ; } else { System.out.println("金额无效" ); } } String moneyStr = "" ; while (true ) { int ge = money % 10 ; String capitalNumber = getCapitalNumber(ge); moneyStr = capitalNumber + moneyStr; money = money / 10 ; if (money == 0 ) { break ; } } int count = 7 - moneyStr.length(); for (int i = 0 ; i < count; i++) { moneyStr = "零" + moneyStr; } System.out.println(moneyStr); String[] arr = {"佰" ,"拾" ,"万" ,"仟" ,"佰" ,"拾" ,"元" }; String result = "" ; for (int i = 0 ; i < moneyStr.length(); i++) { char c = moneyStr.charAt(i); result = result + c + arr[i]; } System.out.println(result); } public static String getCapitalNumber (int number) { String[] arr = {"零" , "壹" , "贰" , "叁" , "肆" , "伍" , "陆" , "柒" , "捌" , "玖" }; return arr[number]; } }
手机号屏蔽 需求:以字符串的形式从键盘接受一个手机号,将中间四位号码屏蔽
最终效果为:131****9468
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class Test8 手机号屏蔽 { public static void main (String[] args) { Scanner sc = new Scanner (System.in); System.out.println("请输入手机号码" ); String phoneNumber = sc.next(); String star = phoneNumber.substring(0 , 3 ); String end = phoneNumber.substring(7 ); String result = star + "****" + end; System.out.println(result); } }
敏感词替换 需求1:键盘录入一个 字符串,如果字符串中包含(TMD),则使用***替换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Test9 敏感词替换 { public static void main (String[] args) { String talk = "后裔你玩什么啊,TMD" ; String result = talk.replace("TMD" , "***" ); System.out.println(talk); System.out.println(result); } }
需求2:如果要替换的敏感词比较多怎么办?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class Test10 多个敏感词替换 { public static void main (String[] args) { Scanner sc = new Scanner (System.in); System.out.println("请输入要说的话" ); String talk = sc.next(); String[] arr = {"TMD" ,"GDX" ,"ctmd" ,"ZZ" ,"lj" ,"FW" ,"nt" }; for (int i = 0 ; i < arr.length; i++) { talk = talk.replace(arr[i],"***" ); } System.out.println(talk); } }
身份证信息查看 身份证的每一位都是有固定的含义:
1、2位:省份
3、4位:城市
5、6位:区县
7-14位:出生年、月、日
15、16位:所在地派出所
17位:性别(奇数男性,偶数女性)
18位:个人信息码(随机产生)
要求打印内容方式如下:
人物信息为:
出生年月日:XXXX年X月X日
性别为:男/女
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.itheima.stringdemo;public class StringDemo11 { public static void main (String[] args) { String id = "321281202001011234" ; String year = id.substring(6 , 10 ); String month = id.substring(10 , 12 ); String day = id.substring(12 , 14 ); System.out.println("人物信息为:" ); System.out.println("出生年月日:" + year + "年" + month + "月" + day + "日" ); char gender = id.charAt(16 ); int num = gender - 48 ; if (num % 2 == 0 ){ System.out.println("性别为:女" ); }else { System.out.println("性别为:男" ); } } }
StringBuilder StringBuilder 可以看成是一个容器,创建之后里面的内容是可变的。
当我们在拼接字符串和反转字符串的时候会使用到
基本使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class StringBuilderDemo3 { public static void main (String[] args) { StringBuilder sb = new StringBuilder ("abc" ); sb.reverse(); int len = sb.length(); System.out.println(len); System.out.println(sb); } }
链式编程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class StringBuilderDemo4 { public static void main (String[] args) { StringBuilder sb = new StringBuilder (); sb.append("aaa" ).append("bbb" ).append("ccc" ).append("ddd" ); System.out.println(sb); String str = sb.toString(); System.out.println(str); } }
练习1:对称字符串 需求:
键盘接受一个字符串,程序判断出该字符串是否是对称字符串,并在控制台打印是或不是
对称字符串:123321、111
非对称字符串:123123
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class StringBuilderDemo6 { public static void main (String[] args) { Scanner sc = new Scanner (System.in); System.out.println("请输入一个字符串" ); String str = sc.next(); String result = new StringBuilder ().append(str).reverse().toString(); if (str.equals(result)){ System.out.println("当前字符串是对称字符串" ); }else { System.out.println("当前字符串不是对称字符串" ); } } }
练习2:拼接字符串 需求:定义一个方法,把 int 数组中的数据按照指定的格式拼接成一个字符串返回。
调用该方法,并在控制台输出结果。
例如:数组为int[] arr = {1,2,3};
执行方法后的输出结果为:[1, 2, 3]
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.itheima.stringbuilderdemo;public class StringBuilderDemo7 { public static void main (String[] args) { int [] arr = {1 ,2 ,3 }; String str = arrToString(arr); System.out.println(str); } public static String arrToString (int [] arr) { StringBuilder sb = new StringBuilder (); sb.append("[" ); for (int i = 0 ; i < arr.length; i++) { if (i == arr.length - 1 ){ sb.append(arr[i]); }else { sb.append(arr[i]).append(", " ); } } sb.append("]" ); return sb.toString(); } }
StringJoiner
StringJoiner跟StringBuilder一样,也可以看成是一个容器,创建之后里面的内容是可变的。
作用:提高字符串的操作效率,而且代码编写特别简洁,但是目前市场上很少有人用。
JDK8出现的
基本使用:
1 2 3 4 5 6 StringJoiner sj = new StringJoiner ("---" );sj.add("aaa" ).add("bbb" ).add("ccc" ); System.out.println(sj);
1 2 3 4 5 6 7 8 9 10 StringJoiner sj = new StringJoiner (", " ,"[" ,"]" );sj.add("aaa" ).add("bbb" ).add("ccc" ); int len = sj.length();System.out.println(len); System.out.println(sj); String str = sj.toString();System.out.println(str);
关于字符串的小扩展:
字符串存储的内存原理
String s = “abc”;直接赋值
特点:
此时字符串abc是存在字符串常量池中的。
先检查字符串常量池中有没有字符串abc,如果有,不会创建新的,而是直接复用。如果没有abc,才会创建一个新的。
所以,直接赋值的方式,代码简单,而且节约内存。
new出来的字符串
看到new关键字,一定是在堆里面开辟了一个小空间。
String s1 = new String(“abc”);
String s2 = “abc”;
s1记录的是new出来的,在堆里面的地址值。
s2是直接赋值的,所以记录的是字符串常量池中的地址值。
==号比较的到底是什么?
如果比较的是基本数据类型:比的是具体的数值是否相等。
如果比较的是引用数据类型:比的是地址值是否相等。
结论:==只能用于比较基本数据类型。不能比较引用数据类型。
ArrayList 集合和数组的优势对比:
长度可变
添加数据的时候不需要考虑索引,默认将数据添加到末尾
ArrayList类概述
ArrayList类常用方法 构造方法
方法名
说明
public ArrayList()
创建一个空的集合对象
成员方法
方法名
说明
public boolean add(要添加的元素)
将指定的元素追加到此集合的末尾
public boolean remove(要删除的元素)
删除指定元素,返回值表示是否删除成功
public E remove(int index)
删除指定索引处的元素,返回被删除的元素
public E set(int index,E element)
修改指定索引处的元素,返回被修改的元素
public E get(int index)
返回指定索引处的元素
public int size()
返回集合中的元素的个数
示例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class ArrayListDemo02 { public static void main (String[] args) { ArrayList<String> array = new ArrayList <String>(); array.add("hello" ); array.add("world" ); array.add("java" ); System.out.println(array.size()); System.out.println("array:" + array); } }
ArrayList存储字符串并遍历 案例需求 创建一个存储字符串的集合,存储3个字符串元素,使用程序实现在控制台遍历该集合
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class ArrayListDemo3 { public static void main (String[] args) { ArrayList<String> list = new ArrayList <>(); list.add("aaa" ); list.add("bbb" ); list.add("ccc" ); list.add("ddd" ); System.out.print("[" ); for (int i = 0 ; i < list.size(); i++) { if (i == list.size() - 1 ){ System.out.print(list.get(i)); }else { System.out.print(list.get(i) + ", " ); } } System.out.print("]" ); } }
ArrayList存储学生对象并遍历 案例需求 创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class ArrayListDemo4 { public static void main (String[] args) { ArrayList<Student> list = new ArrayList <>(); Student s1 = new Student ("zhangsan" ,16 ); Student s2 = new Student ("lisi" ,15 ); Student s3 = new Student ("wangwu" ,18 ); list.add(s1); list.add(s2); list.add(s3); for (int i = 0 ; i < list.size(); i++) { Student stu = list.get(i); System.out.println(stu.getName() + ", " + stu.getAge()); } } }
查找用户的索引 需求:
1,main方法中定义一个集合,存入三个用户对象。
用户属性为:id,username,password
2,要求:定义一个方法,根据id查找对应的学生信息。
如果存在,返回索引
如果不存在,返回-1
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class ArrayListDemo6 { public static void main (String[] args) { ArrayList<User> list = new ArrayList <>(); User u1 = new User ("heima001" , "zhangsan" , "123456" ); User u2 = new User ("heima002" , "lisi" , "1234" ); User u3 = new User ("heima003" , "wangwu" , "1234qwer" ); list.add(u1); list.add(u2); list.add(u3); int index = getIndex(list, "heima001" ); System.out.println(index); } public static int getIndex (ArrayList<User> list, String id) { for (int i = 0 ; i < list.size(); i++) { User u = list.get(i); String uid = u.getId(); if (uid.equals(id)){ return i; } } return -1 ; } }
判断用户的是否存在 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 public class ArrayListDemo5 { public static void main (String[] args) { ArrayList<User> list = new ArrayList <>(); User u1 = new User ("heima001" ,"zhangsan" ,"123456" ); User u2 = new User ("heima002" ,"lisi" ,"12345678" ); User u3 = new User ("heima003" ,"wangwu" ,"1234qwer" ); list.add(u1); list.add(u2); list.add(u3); boolean result = contains(list, "heima001" ); System.out.println(result); } public static boolean contains (ArrayList<User> list, String id) { for (int i = 0 ; i < list.size(); i++) { User u = list.get(i); String uid = u.getId(); if (id.equals(uid)){ return true ; } } return false ; } }
学生管理系统 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 package App;import java.util.ArrayList;import java.util.Scanner;public class StudentSystem { public static void main (String[] args) { ArrayList<Student> list = new ArrayList <>(); loop: while (true ) { System.out.println("-----------------欢迎来到学生管理系统-------------------" ); System.out.println("1:添加学生" ); System.out.println("2:删除学生" ); System.out.println("3:修改学生" ); System.out.println("4:查询学生" ); System.out.println("5:退出" ); System.out.println("请输入您的选择:" ); Scanner sc = new Scanner (System.in); String choose = sc.next(); switch (choose) { case "1" -> addStudent(list); case "2" -> deleteStudent(list); case "3" -> updateStudent(list); case "4" -> queryStudent(list); case "5" -> { System.out.println("退出" ); System.exit(0 ); } default -> System.out.println("没有这个选项" ); } } } public static void addStudent (ArrayList<Student> list) { Student s = new Student (); Scanner sc = new Scanner (System.in); String id = null ; while (true ) { System.out.println("请输入学生的id" ); id = sc.next(); boolean flag = contains(list, id); if (flag){ System.out.println("id已经存在,请重新录入" ); }else { s.setId(id); break ; } } System.out.println("请输入学生的姓名" ); String name = sc.next(); s.setName(name); System.out.println("请输入学生的年龄" ); int age = sc.nextInt(); s.setAge(age); System.out.println("请输入学生的家庭住址" ); String address = sc.next(); s.setAddress(address); list.add(s); System.out.println("学生信息添加成功" ); } public static void deleteStudent (ArrayList<Student> list) { Scanner sc = new Scanner (System.in); System.out.println("请输入要删除的id" ); String id = sc.next(); int index = getIndex(list, id); if (index >= 0 ){ list.remove(index); System.out.println("id为:" + id + "的学生删除成功" ); }else { System.out.println("id不存在,删除失败" ); } } public static void updateStudent (ArrayList<Student> list) { Scanner sc = new Scanner (System.in); System.out.println("请输入要修改学生的id" ); String id = sc.next(); int index = getIndex(list, id); if (index == -1 ){ System.out.println("要修改的id" + id + "不存在,请重新输入" ); return ; } Student stu = list.get(index); System.out.println("请输入要修改的学生姓名" ); String newName = sc.next(); stu.setName(newName); System.out.println("请输入要修改的学生年龄" ); int newAge = sc.nextInt(); stu.setAge(newAge); System.out.println("请输入要修改的学生家庭住址" ); String newAddress = sc.next(); stu.setAddress(newAddress); System.out.println("学生信息修改成功" ); } public static void queryStudent (ArrayList<Student> list) { if (list.size() == 0 ) { System.out.println("当前无学生信息,请添加后再查询" ); return ; } System.out.println("id\t\t姓名\t年龄\t家庭住址" ); for (int i = 0 ; i < list.size(); i++) { Student stu = list.get(i); System.out.println(stu.getId() + "\t" + stu.getName() + "\t" + stu.getAge() + "\t" + stu.getAddress()); } } public static boolean contains (ArrayList<Student> list, String id) { return getIndex(list,id) >= 0 ; } public static int getIndex (ArrayList<Student> list, String id) { for (int i = 0 ; i < list.size(); i++) { Student stu = list.get(i); String sid = stu.getId(); if (sid.equals(id)){ return i; } } return -1 ; } }
static关键字 概述 以前我们定义过如下类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Student { public String name; public char sex; public int age; public Student () { } public Student (String a) { } }
我们已经知道面向对象中,存在类和对象的概念,我们在类中定义了一些成员变量,例如name,age,sex ,结果发现这些成员变量,每个对象都存在(因为每个对象都可以访问)。
而像name ,age , sex确实是每个学生对象都应该有的属性,应该属于每个对象。
所以Java中成员(变量和方法 )等是存在所属性的,Java是通过static关键字来区分的。static关键字在Java开发非常的重要,对于理解面向对象非常关键。
关于 static
关键字的使用,它可以用来修饰的成员变量和成员方法,被static修饰的成员是属于类 的是放在静态区中,没有static修饰的成员变量和方法则是属于对象 的。我们上面案例中的成员变量都是没有static修饰的,所以属于每个对象。
定义格式和使用 static是静态的意思。 static可以修饰成员变量或者修饰方法。
静态变量及其访问 有static修饰成员变量,说明这个成员变量是属于类的,这个成员变量称为类变量 或者静态成员变量 。 直接用 类名访问即可。因为类只有一个,所以静态成员变量在内存区域中也只存在一份。所有的对象都可以共享这个变量。
定义格式
1 修饰符 static 数据类型 变量名 = 初始值;
举例
1 2 3 4 public class Student { public static String schoolName = "播客" ; }
静态成员变量的访问:
格式:类名.静态变量
1 2 3 4 5 public static void main (String[] args) { System.out.println(Student.schoolName); Student.schoolName = "程序员" ; System.out.println(Student.schoolName); }
实例变量及其访问 无static修饰的成员变量属于每个对象的, 这个成员变量叫实例变量 ,之前我们写成员变量就是实例成员变量。
需要注意的是 :实例成员变量属于每个对象,必须创建类的对象才可以访问。
格式:对象.实例成员变量
静态方法及其访问 有static修饰成员方法,说明这个成员方法是属于类的,这个成员方法称为类方法或者 静态方法**。 直接用 类名访问即可。因为类只有一个,所以静态方法在内存区域中也只存在一份。所有的对象都可以共享这个方法。
与静态成员变量一样,静态方法也是直接通过类名.方法名称 即可访问。
举例
1 2 3 4 5 6 7 public class Student { public static String schoolName = "传智播客" ; public static void study () { System.out.println("我们都在黑马程序员学习" ); } }
静态成员变量的访问:
格式:类名.静态方法
1 2 3 public static void main (String[] args) { Student.study(); }
实例方法及其访问 无static修饰的成员方法属于每个对象的,这个成员方法也叫做实例方法 。
需要注意的是 :实例方法是属于每个对象,必须创建类的对象才可以访问。
格式:对象.实例方法
示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Student { private String name ; public void run () { System.out.println("学生可以跑步" ); } public void sleep () { System.out.println("学生睡觉" ); } public static void study () { } }
1 2 3 4 5 6 7 8 public static void main (String[] args) { Student stu = new Student ; stu.name = "徐干" ; stu.sleep(); stu.run(); }
小结 1.当 static
修饰成员变量或者成员方法时,该变量称为静态变量 ,该方法称为静态方法 。该类的每个对象都共享 同一个类的静态变量和静态方法。任何对象都可以更改该静态变量的值或者访问静态方法。但是不推荐这种方式去访问。因为静态变量或者静态方法直接通过类名访问即可,完全没有必要用对象去访问。
2.无static修饰的成员变量或者成员方法,称为实例变量,实例方法 ,实例变量和实例方法必须创建类的对象,然后通过对象来访问。
3.static修饰的成员属于类,会存储在静态区,是随着类的加载而加载的,且只加载一次,所以只有一份,节省内存。存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。它优先于对象存在,所以,可以被所有对象共享。
4.无static修饰的成员,是属于对象,对象有多少个,他们就会出现多少份。所以必须由对象调用。
继承 概述 引入 假如我们要定义如下类: 学生类,老师类和工人类,分析如下。
学生类 属性:姓名,年龄 行为:吃饭,睡觉
老师类 属性:姓名,年龄,薪水 行为:吃饭,睡觉,教书
班主任 属性:姓名,年龄,薪水 行为:吃饭,睡觉,管理
如果我们定义了这三个类去开发一个系统,那么这三个类中就存在大量重复的信息(属性:姓名,年龄。行为:吃饭,睡觉)。这样就导致了相同代码大量重复,代码显得很臃肿和冗余,那么如何解决呢?
假如多个类中存在相同属性和行为时,我们可以将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承 那一个类即可。
其中,多个类可以称为子类 ,单独被继承的那一个类称为父类 、超类(superclass) 或者基类 。
继承的含义 继承描述的是事物之间的所属关系,这种关系是:is-a
的关系。例如,兔子属于食草动物,食草动物属于动物。可见,父类更通用,子类更具体。我们通过继承,可以使多种事物之间形成一种关系体系。
继承 :就是子类继承父类的属性 和行为 ,使得子类对象可以直接具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有 的属性和行为。
继承的好处
提高代码的复用性 (减少代码冗余,相同代码重复利用)。
使类与类之间产生了关系。
继承的格式 通过 extends
关键字,可以声明一个子类继承另外一个父类,定义格式如下:
1 2 3 4 5 6 7 class 父类 { ... } class 子类 extends 父类 { ... }
需要注意:Java是单继承的,一个类只能继承一个直接父类,跟现实世界很像,但是Java中的子类是更加强大的。
继承案例 案例 请使用继承定义以下类:
学生类 属性:姓名,年龄 行为:吃饭,睡觉
老师类 属性:姓名,年龄,薪水 行为:吃饭,睡觉,教书
班主任 属性:姓名,年龄,薪水 行为:吃饭,睡觉,管理
案例代码实现 1.父类Human类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Human { private String name ; private 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; } }
2.子类Teacher类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Teacher extends Human { private double salary ; public void teach () { System.out.println("老师在认真教技术!" ); } public double getSalary () { return salary; } public void setSalary (double salary) { this .salary = salary; } }
3.子类Student类
1 2 3 public class Student extends Human { }
4.子类BanZhuren类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Teacher extends Human { private double salary ; public void admin () { System.out.println("班主任强调纪律问题!" ); } public double getSalary () { return salary; } public void setSalary (double salary) { this .salary = salary; } }
5.测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Test { public static void main (String[] args) { Teacher dlei = new Teacher (); dlei.setName("播仔" ); dlei.setAge("31" ); dlei.setSalary(1000.99 ); System.out.println(dlei.getName()); System.out.println(dlei.getAge()); System.out.println(dlei.getSalary()); dlei.teach(); BanZhuRen linTao = new BanZhuRen (); linTao.setName("灵涛" ); linTao.setAge("28" ); linTao.setSalary(1000.99 ); System.out.println(linTao.getName()); System.out.println(linTao.getAge()); System.out.println(linTao.getSalary()); linTao.admin(); Student xugan = new Student (); xugan.setName("播仔" ); xugan.setAge("31" ); System.out.println(xugan.getName()); System.out.println(xugan.getAge()); } }
小结
继承实际上是子类相同的属性和行为可以定义在父类中,子类特有的属性和行为由自己定义,这样就实现了相同属性和行为的重复利用,从而提高了代码复用。
子类继承父类,就可以直接得到父类的成员变量和方法。是否可以继承所有成分呢?请看下节!
子类不能继承的内容 引入 并不是父类的所有内容都可以给子类继承的:
子类不能继承父类的构造方法。
值得注意的是子类可以继承父类的私有成员(成员变量,方法),只是子类无法直接访问而已,可以通过getter/setter方法访问父类的private成员变量。
演示代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class Demo03 { public static void main (String[] args) { Zi z = new Zi (); System.out.println(z.num1); System.out.println(z.getNum2()); z.show1(); } } class Fu { public int num1 = 10 ; private int num2 = 20 ; public void show1 () { System.out.println("show1" ); } private void show2 () { System.out.println("show2" ); } public int getNum2 () { return num2; } public void setNum2 (int num2) { this .num2 = num2; } } class Zi extends Fu {}
继承后的特点—成员变量 当类之间产生了继承关系后,其中各类中的成员变量,又产生了哪些影响呢?
成员变量不重名 如果子类父类中出现不重名 的成员变量,这时的访问是没有影响的 。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Fu { int num = 5 ; } class Zi extends Fu { int num2 = 6 ; public void show () { System.out.println("Fu num=" +num); System.out.println("Zi num2=" +num2); } } class Demo04 { public static void main (String[] args) { Zi z = new Zi (); z.show(); } } 演示结果: Fu num = 5 Zi num2 = 6
成员变量重名 如果子类父类中出现重名 的成员变量,这时的访问是有影响的 。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Fu1 { int num = 5 ; } class Zi1 extends Fu1 { int num = 6 ; public void show () { System.out.println("Fu num=" + num); System.out.println("Zi num=" + num); } } class Demo04 { public static void main (String[] args) { Zi1 z = new Zi1 (); z1.show(); } } 演示结果: Fu num = 6 Zi num = 6
子父类中出现了同名的成员变量时,子类会优先访问自己对象中的成员变量。如果此时想访问父类成员变量如何解决呢?我们可以使用super关键字。
super访问父类成员变量 子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用super
关键字,修饰父类成员变量,类似于之前学过的 this
。
需要注意的是:super代表的是父类对象的引用,this代表的是当前对象的引用。
使用格式:
子类方法需要修改,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Fu { int num = 5 ; } class Zi extends Fu { int num = 6 ; public void show () { int num = 1 ; System.out.println("method num=" + num); System.out.println("Zi num=" + this .num); System.out.println("Fu num=" + super .num); } } class Demo04 { public static void main (String[] args) { Zi1 z = new Zi1 (); z1.show(); } } 演示结果: method num=1 Zi num=6 Fu num=5
小贴士:Fu 类中的成员变量是非私有的,子类中可以直接访问。若Fu 类中的成员变量私有了,子类是不能直接访问的。通常编码时,我们遵循封装的原则,使用private修饰成员变量,那么如何访问父类的私有成员变量呢?对!可以在父类中提供公共的getXxx方法和setXxx方法。
继承后的特点—成员方法 当类之间产生了关系,其中各类中的成员方法,又产生了哪些影响呢?
成员方法不重名 如果子类父类中出现不重名 的成员方法,这时的调用是没有影响的 。对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Fu { public void show () { System.out.println("Fu类中的show方法执行" ); } } class Zi extends Fu { public void show2 () { System.out.println("Zi类中的show2方法执行" ); } } public class Demo05 { public static void main (String[] args) { Zi z = new Zi (); z.show(); z.show2(); } }
成员方法重名 如果子类父类中出现重名 的成员方法,则创建子类对象调用该方法的时候,子类对象会优先调用自己的方法。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Fu { public void show () { System.out.println("Fu show" ); } } class Zi extends Fu { public void show () { System.out.println("Zi show" ); } } public class ExtendsDemo05 { public static void main (String[] args) { Zi z = new Zi (); z.show(); } }
方法重写 概念 方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现 。
使用场景与案例 发生在子父类之间的关系。
子类继承了父类的方法,但是子类觉得父类的这方法不足以满足自己的需求,子类重新写了一个与父类同名的方法,以便覆盖父类的该方法。
例如:我们定义了一个动物类代码如下:
1 2 3 4 5 6 7 8 public class Animal { public void run () { System.out.println("动物跑的很快!" ); } public void cry () { System.out.println("动物都可以叫~~~" ); } }
然后定义一个猫类,猫可能认为父类cry()方法不能满足自己的需求
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Cat extends Animal { public void cry () { System.out.println("我们一起学猫叫,喵喵喵!喵的非常好听!" ); } } public class Test { public static void main (String[] args) { Cat ddm = new Cat (); ddm.run(); ddm.cry(); } }
@Override重写注解
注意事项
方法重写是发生在子父类之间的关系。
子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。
继承后的特点—构造方法 引入 当类之间产生了关系,其中各类中的构造方法,又产生了哪些影响呢? 首先我们要回忆两个事情,构造方法的定义格式和作用。
构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
构造方法的作用是初始化对象成员变量数据的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个super()
,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。(先有爸爸,才能有儿子 )
继承后子类构方法器特点:子类所有构造方法的第一行都会默认先调用父类的无参构造方法
案例演示 按如下需求定义类:
人类 成员变量: 姓名,年龄 成员方法: 吃饭
学生类 成员变量: 姓名,年龄,成绩 成员方法: 吃饭
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class Person { private String name; private int age; public Person () { System.out.println("父类无参" ); } } class Student extends Person { private double score; public Student () { System.out.println("子类无参" ); } public Student (double score) { this .score = score; System.out.println("子类有参" ); } } public class Demo07 { public static void main (String[] args) { Student s1 = new Student (); System.out.println("----------" ); Student s2 = new Student (99.9 ); } } 输出结果: 父类无参 子类无参 ---------- 父类无参 子类有参
小结
子类构造方法执行的时候,都会在第一行默认先调用父类无参数构造方法一次。
子类构造方法的第一行都隐含了一个**super()去调用父类无参数构造方法, super()**可以省略不写。
super(…)和this(…) 引入 请看上节中的如下案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Person { private String name; private int age; public Person () { System.out.println("父类无参" ); } } class Student extends Person { private double score; public Student () { System.out.println("子类无参" ); } public Student (double score) { this .score = score; System.out.println("子类有参" ); } } public class Demo07 { public static void main (String[] args) { Student s2 = new Student (99.9 ); System.out.println(s2.getScore()); System.out.println(s2.getName()); System.out.println(s2.getAge()); } }
我们发现,子类有参数构造方法只是初始化了自己对象中的成员变量score,而父类中的成员变量name和age依然是没有数据的,怎么解决这个问题呢,我们可以借助与super(…)去调用父类构造方法,以便初始化继承自父类对象的name和age.
super和this的用法格式 super和this完整的用法如下,其中this,super访问成员我们已经接触过了。
1 2 3 4 5 this .成员变量 -- 本类的super .成员变量 -- 父类的this .成员方法名() -- 本类的 super .成员方法名() -- 父类的
接下来我们使用调用构造方法格式:
1 2 super (...) -- 调用父类的构造方法,根据参数匹配确认this (...) -- 调用本类的其他构造方法,根据参数匹配确认
super(….)用法演示 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 class Person { private String name = "凤姐" ; private int age = 20 ; public Person () { System.out.println("父类无参" ); } public Person (String name , int age) { this .name = name ; this .age = age ; } } class Student extends Person { private double score = 100 ; public Student () { System.out.println("子类无参" ); } public Student (String name , int age,double score) { super (name ,age); this .score = score; System.out.println("子类有参" ); } } public class Demo07 { public static void main (String[] args) { Student s2 = new Student ("张三" ,20 ,99 ); System.out.println(s2.getScore()); System.out.println(s2.getName()); System.out.println(s2.getAge()); } }
注意:
子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。
super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
super(..)是根据参数去确定调用父类哪个构造方法的。
super(…)案例图解 父类空间优先于子类对象产生
在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构造七调用时,一定先调用父类的构造方法。理解图解如下:
this(…)用法演示 this(…)
默认是去找本类中的其他构造方法,根据参数来确定具体调用哪一个构造方法。
为了借用其他构造方法的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.itheima._08this和super 调用构造方法;public class ThisDemo01 { public static void main (String[] args) { Student xuGan = new Student (); System.out.println(xuGan.getName()); System.out.println(xuGan.getAge()); System.out.println(xuGan.getSex()); } } class Student { private String name ; private int age ; private char sex ; public Student () { this ("徐干" ,21 ,'男' ); } public Student (String name, int age, char sex) { this .name = name ; this .age = age ; this .sex = sex ; } 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 char getSex () { return sex; } public void setSex (char sex) { this .sex = sex; } }
小结
子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。
super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
super(..)和this(…)是根据参数去确定调用父类哪个构造方法的。
super(..)可以调用父类构造方法初始化继承自父类的成员变量的数据。
this(..)可以调用本类中的其他构造方法。
继承的特点
Java只支持单继承,不支持多继承。
1 2 3 4 5 class A {}class B {}class C1 extends A {}
一个类可以有多个子类。
1 2 3 4 class A {}class C1 extends A {}class C2 extends A {}
可以多层继承。
1 2 3 class A {}class C1 extends A {}class D extends C1 {}
顶层父类是Object类。所有的类默认继承Object,作为父类。
4. 知识小结: 会写一个继承结构下的标准Javabean即可
需求:
猫:属性,姓名,年龄,颜色
狗:属性,姓名,年龄,颜色,吼叫
分享书写技巧:
1.在大脑中要区分谁是父,谁是子
2.把共性写到父类中,独有的东西写在子类中
3.开始编写标准Javabean(从上往下写)
4.在测试类中,创建对象并赋值调用
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 package com.itheima.test4;public class Animal { private String name; private int age; private String color; public Animal () { } public Animal (String name, int age, String color) { this .name = name; this .age = age; this .color = color; } 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 String getColor () { return color; } public void setColor (String color) { this .color = color; } } public class Cat extends Animal { public Cat () { } public Cat (String name, int age, String color) { super (name,age,color); } } public class Dog extends Animal { private String wang; public Dog () { } public Dog (String name, int age, String color,String wang) { super (name,age,color); this .wang = wang; } public String getWang () { return wang; } public void setWang (String wang) { this .wang = wang; } } public class Demo { public static void main (String[] args) { Dog d = new Dog ("旺财" ,2 ,"黑色" ,"嗷呜~~" ); System.out.println(d.getName()+", " + d.getAge() + ", " + d.getColor() + ", " + d.getWang()); Cat c = new Cat ("中华田园猫" ,3 ,"黄色" ); System.out.println(c.getName() + ", " + c.getAge() + ", " + c.getColor()); } }
多态 多态的形式 多态是继封装、继承之后,面向对象的第三大特性。
多态是出现在继承或者实现关系中的
多态体现的格式 :
1 2 父类类型 变量名 = new 子类/实现类构造器; 变量名.方法名();
多态的前提 :有继承关系,子类对象是可以赋值给父类类型的变量。例如Animal是一个动物类型,而Cat是一个猫类型。Cat继承了Animal,Cat对象也是Animal类型,自然可以赋值给父类类型的变量。
多态的使用场景 如果没有多态,在下图中register方法只能传递学生对象,其他的Teacher和administrator对象是无法传递给register方法方法的,在这种情况下,只能定义三个不同的register方法分别接收学生,老师和管理员。
有了多态之后,方法的形参就可以定义为共同的父类Person。
要注意的是:
当一个方法的形参是一个类,我们可以传递这个类所有的子类对象。
当一个方法的形参是一个接口,我们可以传递这个接口所有的实现类对象(后面会学)。
而且多态还可以根据传递的不同对象来调用不同类中的方法。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 父类: public class Person { private String name; private int age; 空参构造 带全部参数的构造 get和set方法 public void show () { System.out.println(name + ", " + age); } } 子类1 : public class Administrator extends Person { @Override public void show () { System.out.println("管理员的信息为:" + getName() + ", " + getAge()); } } 子类2 : public class Student extends Person { @Override public void show () { System.out.println("学生的信息为:" + getName() + ", " + getAge()); } } 子类3 : public class Teacher extends Person { @Override public void show () { System.out.println("老师的信息为:" + getName() + ", " + getAge()); } } 测试类: public class Test { public static void main (String[] args) { Student s = new Student (); s.setName("张三" ); s.setAge(18 ); Teacher t = new Teacher (); t.setName("王建国" ); t.setAge(30 ); Administrator admin = new Administrator (); admin.setName("管理员" ); admin.setAge(35 ); register(s); register(t); register(admin); } public static void register (Person p) { p.show(); } }
多态的定义和前提 多态 : 是指同一行为,具有多个不同表现形式。
从上面案例可以看出,Cat和Dog都是动物,都是吃这一行为,但是出现的效果(表现形式)是不一样的。
前提【重点】
有继承或者实现关系
方法的重写【意义体现:不重写,无意义】
父类引用指向子类对象【格式体现】
父类类型:指子类对象继承的父类类型,或者实现的父接口类型。
多态的运行特点 调用成员变量时:编译看左边,运行看左边
调用成员方法时:编译看左边,运行看右边
代码示例:
1 2 3 4 5 6 7 Fu f = new Zi ();System.out.println(f.name); f.show();
多态的弊端 我们已经知道多态编译阶段是看左边父类类型的,如果子类有些独有的功能,此时多态的写法就无法访问子类独有功能了 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Animal { public void eat () { System.out.println("动物吃东西!" ) } } class Cat extends Animal { public void eat () { System.out.println("吃鱼" ); } public void catchMouse () { System.out.println("抓老鼠" ); } } class Dog extends Animal { public void eat () { System.out.println("吃骨头" ); } } class Test { public static void main (String[] args) { Animal a = new Cat (); a.eat(); a.catchMouse(); } }
引用类型转换 为什么要转型 多态的写法就无法访问子类独有功能了。
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用 子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点”小麻烦”。所以,想要调用子类特有的方法,必须做向下转型。
回顾基本数据类型转换
自动转换: 范围小的赋值给范围大的.自动完成:double d = 5;
强制转换: 范围大的赋值给范围小的,强制转换:int i = (int)3.14
多态的转型分为向上转型(自动转换)与向下转型(强制转换)两种。
向上转型(自动转换)
向上转型 :多态本身是子类类型向父类类型向上转换(自动转换)的过程,这个过程是默认的。 当父类引用指向一个子类对象时,便是向上转型。 使用格式:
1 2 父类类型 变量名 = new 子类类型(); 如:Animal a = new Cat ();
原因是:父类类型相对与子类来说是大范围的类型,Animal是动物类,是父类类型。Cat是猫类,是子类类型。Animal类型的范围当然很大,包含一切动物。 所以子类范围小可以直接自动转型给父类类型的变量。
向下转型(强制转换)
向下转型 :父类类型向子类类型向下转换的过程,这个过程是强制的。 一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。
使用格式:
1 2 3 子类类型 变量名 = (子类类型) 父类变量名; 如:Aniaml a = new Cat (); Cat c = (Cat) a;
案例演示 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用 子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点”小麻烦”。所以,想要调用子类特有的方法,必须做向下转型。
转型演示,代码如下:
定义类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 abstract class Animal { abstract void eat () ; } class Cat extends Animal { public void eat () { System.out.println("吃鱼" ); } public void catchMouse () { System.out.println("抓老鼠" ); } } class Dog extends Animal { public void eat () { System.out.println("吃骨头" ); } public void watchHouse () { System.out.println("看家" ); } }
定义测试类:
1 2 3 4 5 6 7 8 9 10 11 public class Test { public static void main (String[] args) { Animal a = new Cat (); a.eat(); Cat c = (Cat)a; c.catchMouse(); } }
转型的异常 转型的过程中,一不小心就会遇到这样的问题,请看如下代码:
1 2 3 4 5 6 7 8 9 10 11 public class Test { public static void main (String[] args) { Animal a = new Cat (); a.eat(); Dog d = (Dog)a; d.watchHouse(); } }
这段代码可以通过编译,但是运行时,却报出了 ClassCastException
,类型转换异常!这是因为,明明创建了Cat类型对象,运行时,当然不能转换成Dog对象的。
instanceof关键字 为了避免ClassCastException的发生,Java提供了 instanceof
关键字,给引用变量做类型的校验,格式如下:
1 2 3 变量名 instanceof 数据类型 如果变量属于该数据类型或者其子类类型,返回true 。 如果变量不属于该数据类型或者其子类类型,返回false 。
所以,转换前,我们最好先做一个判断,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Test { public static void main (String[] args) { Animal a = new Cat (); a.eat(); if (a instanceof Cat){ Cat c = (Cat)a; c.catchMouse(); } else if (a instanceof Dog){ Dog d = (Dog)a; d.watchHouse(); } } }
instanceof新特性 JDK14的时候提出了新特性,把判断和强转合并成了一行
1 2 3 4 5 6 7 8 9 10 if (a instanceof Dog d){ d.lookHome(); }else if (a instanceof Cat c){ c.catchMouse(); }else { System.out.println("没有这个类型,无法转换" ); }
包 包 包在操作系统中其实就是一个文件夹。包是用来分门别类的管理技术,不同的技术类放在不同的包下 ,方便管理和维护。
包名的命名规范 :
导包 什么时候需要导包?
情况一:在使用Java中提供的非核心包中的类时
情况二:使用自己写的其他包中的类时
什么时候不需要导包?
情况一:在使用Java核心包(java.lang)中的类时
情况二:在使用自己写的同一个包中的类时
使用不同包下的相同类怎么办? 假设demo1和demo2中都有一个Student该如何使用?
代码示例:
1 2 3 4 5 com.itheima.homework.demo1.Student s1 = new com .itheima.homework.demo1.Student(); com.itheima.homework.demo2.Student s2 = new com .itheima.homework.demo2.Student();
权限修饰符 权限修饰符 在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限,我们之前已经学习过了public 和 private,接下来我们研究一下protected和默认修饰符的作用。
不同权限的访问能力
public
protected
默认
private
同一类中
√
√
√
√
同一包中的类
√
√
√
不同包的子类
√
√
不同包中的无关类
√
可见,public具有最大权限。private则是最小权限。
编写代码时,如果没有特殊的考虑,建议这样使用权限:
成员变量使用private
,隐藏细节。
构造方法使用 public
,方便创建对象。
成员方法使用public
,方便调用方法。
小贴士:不加权限修饰符,就是默认权限
final关键字 概述 学习了继承后,我们知道,子类可以在父类的基础上改写父类内容,比如,方法重写。
如果有一个方法我不想别人去改写里面内容,该怎么办呢?
Java提供了final
关键字,表示修饰的内容不可变。
final : 不可改变,最终的含义。可以用于修饰类、方法和变量。
类:被修饰的类,不能被继承。
方法:被修饰的方法,不能被重写。
变量:被修饰的变量,有且仅能被赋值一次。
使用方式 修饰类 final修饰的类,不能被继承。
格式如下:
代码:
查询API发现像 public final class String
、public final class Math
、public final class Scanner
等,很多我们学习过的类,都是被final修饰的,目的就是供我们使用,而不让我们所以改变其内容。
修饰方法 final修饰的方法,不能被重写。 格式如下:
1 2 3 修饰符 final 返回值类型 方法名(参数列表){ }
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Fu2 { final public void show1 () { System.out.println("Fu2 show1" ); } public void show2 () { System.out.println("Fu2 show2" ); } } class Zi2 extends Fu2 { @Override public void show2 () { System.out.println("Zi2 show2" ); } }
修饰变量-局部变量
局部变量——基本类型 基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class FinalDemo1 { public static void main (String[] args) { final int a; a = 10 ; a = 20 ; final int b = 10 ; b = 20 ; } }
思考,下面两种写法,哪种可以通过编译?
写法1:
1 2 3 4 5 final int c = 0 ;for (int i = 0 ; i < 10 ; i++) { c = i; System.out.println(c); }
写法2:
1 2 3 4 for (int i = 0 ; i < 10 ; i++) { final int c = i; System.out.println(c); }
根据 final
的定义,写法1报错!写法2,为什么通过编译呢?因为每次循环,都是一次新的变量c。这也是大家需要注意的地方。
修饰变量-成员变量 成员变量涉及到初始化的问题,初始化方式有显示初始化和构造方法初始化,只能选择其中一个:
显示初始化(在定义成员变量的时候立马赋值)(常用);
1 2 3 public class Student { final int num = 10 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Student { final int num = 10 ; final int num2; public Student () { this .num2 = 20 ; } public Student (String name) { this .num2 = 20 ; } }
被final修饰的常量名称,一般都有书写规范,所有字母都大写 。
抽象类 概述 抽象类引入 父类中的方法,被它的子类们重写,子类各自的实现都不尽相同。那么父类的方法声明和方法主体,只有声明还有意义,而方法主体则没有存在的意义了(因为子类对象会调用自己重写的方法)。换句话说,父类可能知道子类应该有哪个功能,但是功能具体怎么实现父类是不清楚的(由子类自己决定),父类只需要提供一个没有方法体的定义即可,具体实现交给子类自己去实现。我们把没有方法体的方法称为抽象方法。Java语法规定,包含抽象方法的类就是抽象类 。
抽象方法 : 没有方法体的方法。
抽象类 :包含抽象方法的类。
abstract使用格式 abstract是抽象的意思,用于修饰方法方法和类,修饰的方法是抽象方法,修饰的类是抽象类。
抽象方法 使用abstract
关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
定义格式:
1 修饰符 abstract 返回值类型 方法名 (参数列表);
代码举例:
1 public abstract void run () ;
抽象类 如果一个类包含抽象方法,那么该类必须是抽象类。注意:抽象类不一定有抽象方法,但是有抽象方法的类必须定义成抽象类。
定义格式:
1 2 3 abstract class 类名字 { }
代码举例:
1 2 3 public abstract class Animal { public abstract void run () ; }
抽象类的使用 要求 :继承抽象类的子类必须重写父类所有的抽象方法 。否则,该子类也必须声明为抽象类。
代码举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 abstract class Employee { private String id; private String name; private double salary; public Employee () { } public Employee (String id, String name, double salary) { this .id = id; this .name = name; this .salary = salary; } abstract public void work () ; } class Manager extends Employee { public Manager () { } public Manager (String id, String name, double salary) { super (id, name, salary); } @Override public void work () { System.out.println("管理其他人" ); } } class Cook extends Employee { public Cook () { } public Cook (String id, String name, double salary) { super (id, name, salary); } @Override public void work () { System.out.println("厨师炒菜多加点盐..." ); } } public class Demo10 { public static void main (String[] args) { Manager m = new Manager (); m.work(); Cook c = new Cook ("ap002" , "库克" , 1 ); c.work(); } }
此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法 。
抽象类的特征 抽象类的特征总结起来可以说是 有得有失
有得:抽象类得到了拥有抽象方法的能力。
有失:抽象类失去了创建对象的能力。
其他成员(构造方法,实例方法,静态方法等)抽象类都是具备的。
抽象类的细节 不需要背,只要当idea报错之后,知道如何修改即可。
关于抽象类的使用,以下为语法上要注意的细节,虽然条目较多,但若理解了抽象的本质,无需死记硬背。
抽象类不能创建对象 ,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。
抽象类的子类,必须重写抽象父类中所有的 抽象方法,否则子类也必须定义成抽象类,编译无法通过而报错。
理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。
抽象类存在的意义是为了被子类继承。
理解:抽象类中已经实现的是模板中确定的成员,抽象类不确定如何实现的定义成抽象方法,交给具体的子类去实现。
抽象类存在的意义 抽象类存在的意义是为了被子类继承,否则抽象类将毫无意义。抽象类可以强制让子类,一定要按照规定的格式进行重写。
接口 概述 我们已经学完了抽象类,抽象类中可以用抽象方法,也可以有普通方法,构造方法,成员变量等。那么什么是接口呢?接口是更加彻底的抽象,JDK7之前,包括JDK7,接口中全部是抽象方法。接口同样是不能创建对象的 。
定义格式
接口成分的特点 在JDK7,包括JDK7之前,接口中的只有 包含:抽象方法和常量
抽象方法 注意:接口中的抽象方法默认会自动加上public abstract修饰程序员无需自己手写!! 按照规范:以后接口中的抽象方法建议不要写上public abstract。因为没有必要啊,默认会加上。
常量 在接口中定义的成员变量默认会加上: public static final修饰。也就是说在接口中定义的成员变量实际上是一个常量。这里是使用public static final修饰后,变量值就不可被修改,并且是静态化的变量可以直接用接口名访问,所以也叫常量。常量必须要给初始值。常量命名规范建议字母全部大写,多个单词用下划线连接。
案例演示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public interface InterF { void run () ; String getName () ; int add (int a , int b) ; int AGE = 12 ; String SCHOOL_NAME = "程序员" ; }
基本的实现 实现接口的概述 类与接口的关系为实现关系,即类实现接口 ,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements
关键字。
实现接口的格式 1 2 3 4 5 6 class 类名 implements 接口1 ,接口2 ,接口3. ..{ }
从上面格式可以看出,接口是可以被多实现的。大家可以想一想为什么呢?
类实现接口的要求和意义
必须重写实现的全部接口中所有抽象方法。
如果一个类实现了接口,但是没有重写完全部接口的全部抽象方法,这个类也必须定义成抽象类。
意义:接口体现的是一种规范,接口对实现类是一种强制性的约束,要么全部完成接口申明的功能,要么自己也定义成抽象类。这正是一种强制性的规范。
类与接口基本实现案例 假如我们定义一个运动员的接口 (规范),代码如下:
1 2 3 4 5 6 7 8 public interface SportMan { void run () ; void law () ; String compittion (String project) ; }
接下来定义一个乒乓球运动员类,实现接口,实现接口的实现类 代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.itheima._03接口的实现;public class PingPongMan implements SportMan { @Override public void run () { System.out.println("乒乓球运动员稍微跑一下!!" ); } @Override public void law () { System.out.println("乒乓球运动员守法!" ); } @Override public String compittion (String project) { return "参加" +project+"得金牌!" ; } }
测试代码 :
1 2 3 4 5 6 7 8 9 10 public class TestMain { public static void main (String[] args) { PingPongMan zjk = new PingPongMan (); zjk.run(); zjk.law(); System.out.println(zjk.compittion("全球乒乓球比赛" )); } }
类与接口的多实现案例 类与接口之间的关系是多实现的,一个类可以同时实现多个接口。
首先我们先定义两个接口,代码如下:
1 2 3 4 5 6 7 8 9 10 public interface Law { void rule () ; } public interface SportMan { void run () ; }
然后定义一个实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class JumpMan implements Law ,SportMan { @Override public void rule () { System.out.println("尊长守法" ); } @Override public void run () { System.out.println("训练跑步!" ); } }
从上面可以看出类与接口之间是可以多实现的,我们可以理解成实现多个规范,这是合理的。
接口与接口的多继承 Java中,接口与接口之间是可以多继承的:也就是一个接口可以同时继承多个接口。大家一定要注意:
类与接口是实现关系
接口与接口是继承关系
接口继承接口就是把其他接口的抽象方法与本接口进行了合并。
案例演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public interface Abc { void go () ; void test () ; } public interface Law { void rule () ; void test () ; } * * 总结: * 接口与类之间是多实现的。 * 接口与接口之间是多继承的。 * */ public interface SportMan extends Law , Abc { void run () ; }
扩展:接口的细节 不需要背,只要当idea报错之后,知道如何修改即可。
关于接口的使用,以下为语法上要注意的细节,虽然条目较多,但若理解了抽象的本质,无需死记硬背。
当两个接口中存在相同抽象方法的时候,该怎么办?
只要重写一次即可。此时重写的方法,既表示重写1接口的,也表示重写2接口的。
实现类能不能继承A类的时候,同时实现其他接口呢?
继承的父类,就好比是亲爸爸一样 实现的接口,就好比是干爹一样 可以继承一个类的同时,再实现多个接口,只不过,要把接口里面所有的抽象方法,全部实现。
实现类能不能继承一个抽象类的时候,同时实现其他接口呢?
实现类可以继承一个抽象类的同时,再实现其他多个接口,只不过要把里面所有的抽象方法全部重写。
实现类Zi,实现了一个接口,还继承了一个Fu类。假设在接口中有一个方法,父类中也有一个相同的方法。子类如何操作呢?
处理办法一:如果父类中的方法体,能满足当前业务的需求,在子类中可以不用重写。 处理办法二:如果父类中的方法体,不能满足当前业务的需求,需要在子类中重写。
如果一个接口中,有10个抽象方法,但是我在实现类中,只需要用其中一个,该怎么办?
可以在接口跟实现类中间,新建一个中间类(适配器类) 让这个适配器类去实现接口,对接口里面的所有的方法做空重写。 让子类继承这个适配器类,想要用到哪个方法,就重写哪个方法。 因为中间类没有什么实际的意义,所以一般会把中间类定义为抽象的,不让外界创建对象
内部类 概述 什么是内部类 将一个类A定义在另一个类B里面,里面的那个类A就称为内部类 ,B则称为外部类 。可以把内部类理解成寄生,外部类理解成宿主。
什么时候使用内部类 一个事物内部还有一个独立的事物,内部的事物脱离外部的事物无法独立使用
人里面有一颗心脏。
汽车内部有一个发动机。
为了实现更好的封装性。
内部类的分类 按定义的位置来分
成员内部内 ,类定义在了成员位置 (类中方法外称为成员位置,无static修饰的内部类)
静态内部类 ,类定义在了成员位置 (类中方法外称为成员位置,有static修饰的内部类)
局部内部类 ,类定义在方法内
匿名内部类 ,没有名字的内部类,可以在方法中,也可以在类中方法外。
成员内部类 成员内部类特点 :
无static修饰的内部类,属于外部类对象的。
宿主:外部类对象。
内部类的使用格式 :
获取成员内部类对象的两种方式 :
方式一:外部直接创建成员内部类的对象
1 外部类.内部类 变量 = new 外部类().new 内部类();
方式二:在外部类中定义一个方法提供内部类的对象
案例演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 方式一: public class Test { public static void main (String[] args) { Outer.Inner oi = new Outer ().new Inner (); oi.method(); } } class Outer { public class Inner { public void method () { System.out.println("内部类中的方法被调用了" ); } } } 方式二: public class Outer { String name; private class Inner { static int a = 10 ; } public Inner getInstance () { return new Inner (); } } public class Test { public static void main (String[] args) { Outer o = new Outer (); System.out.println(o.getInstance()); } }
成员内部类的细节 编写成员内部类的注意点:
成员内部类可以被一些修饰符所修饰,比如: private,默认,protected,public,static等
在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量。
创建内部类对象时,对象中有一个隐含的Outer.this记录外部类对象的地址值。(请参见3.6节的内存图)
详解:
内部类被private修饰,外界无法直接获取内部类的对象,只能通过3.3节中的方式二获取内部类的对象
被其他权限修饰符修饰的内部类一般用3.3节中的方式一直接获取内部类的对象
内部类被static修饰是成员内部类中的特殊情况,叫做静态内部类下面单独学习。
内部类如果想要访问外部类的成员变量,外部类的变量必须用final修饰,JDK8以前必须手动写final,JDK8之后不需要手动写,JDK默认加上。
成员内部类面试题 请在?地方向上相应代码,以达到输出的内容
注意:内部类访问外部类对象的格式是:外部类名.this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Test { public static void main (String[] args) { Outer.inner oi = new Outer ().new inner (); oi.method(); } } class Outer { private int a = 30 ; class inner { private int a = 20 ; public void method () { int a = 10 ; System.out.println(???); System.out.println(???); System.out.println(???); } } }
成员内部类内存图 静态内部类 静态内部类特点 :
有static修饰,属于外部类本身的。
总结:静态内部类与其他类的用法完全一样。只是访问的时候需要加上外部类.内部类。
拓展1 :静态内部类可以直接访问外部类的静态成员。
拓展2 :静态内部类不可以直接访问外部类的非静态成员,如果要访问需要创建外部类的对象。
拓展3 :静态内部类中没有银行的Outer.this。
内部类的使用格式 :
静态内部类对象的创建格式 :
1 外部类.内部类 变量 = new 外部类.内部类构造器;
调用方法的格式:
调用非静态方法的格式:先创建对象,用对象调用
调用静态方法的格式:外部类名.内部类名.方法名();
案例演示 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Outer01 { private static String sc_name = "黑马程序" ; public static class Inner01 { private String name; public Inner01 (String name) { this .name = name; } public void showName () { System.out.println(this .name); System.out.println(sc_name); } } } public class InnerClassDemo01 { public static void main (String[] args) { Outer01.Inner01 in = new Outer01 .Inner01("张三" ); in.showName(); } }
局部内部类
定义格式:
1 2 3 4 5 6 7 8 9 10 11 class 外部类名 { 数据类型 变量名; 修饰符 返回值类型 方法名(参数列表) { class 内部类 { } } }
匿名内部类【重点】 概述 匿名内部类 :是内部类的简化写法。他是一个隐含了名字的内部类。开发中,最常用到的内部类就是匿名内部类了。
格式 1 2 3 new 类名或者接口名() { 重写方法; };
包含了:
所以从语法上来讲,这个整体其实是匿名内部类对象
什么时候用到匿名内部类 实际上,如果我们希望定义一个只要使用一次的类,就可考虑使用匿名内部类。匿名内部类的本质作用
是为了简化代码 。
之前我们使用接口时,似乎得做如下几步操作:
定义子类
重写接口中的方法
创建子类对象
调用重写后的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 interface Swim { public abstract void swimming () ; } class Student implements Swim { @Override public void swimming () { System.out.println("狗刨式..." ); } } public class Test { public static void main (String[] args) { Student s = new Student (); s.swimming(); } }
我们的目的,最终只是为了调用方法,那么能不能简化一下,把以上四步合成一步呢?匿名内部类就是做这样的快捷方式。
匿名内部类前提和格式 匿名内部类必须继承一个父类 或者实现一个父接口 。
匿名内部类格式
1 2 3 4 5 6 7 new 父类名或者接口名(){ @Override public void method () { } };
使用方式 以接口为例,匿名内部类的使用,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 interface Swim { public abstract void swimming () ; } public class Demo07 { public static void main (String[] args) { new Swim () { @Override public void swimming () { System.out.println("自由泳..." ); } }.swimming(); Swim s2 = new Swim () { @Override public void swimming () { System.out.println("蛙泳..." ); } }; s2.swimming(); s2.swimming(); } }
匿名内部类的特点
定义一个没有名字的内部类
这个类实现了父类,或者父类接口
匿名内部类会创建这个没有名字的类的对象
匿名内部类的使用场景 通常在方法的形式参数是接口或者抽象类时,也可以将匿名内部类作为参数传递。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 interface Swim { public abstract void swimming () ; } public class Demo07 { public static void main (String[] args) { Student s = new Student (); goSwimming(s); Swim s3 = new Swim () { @Override public void swimming () { System.out.println("蝶泳..." ); } }; goSwimming(s3); goSwimming(new Swim () { public void swimming () { System.out.println("大学生, 蛙泳..." ); } }); goSwimming(new Swim () { public void swimming () { System.out.println("小学生, 自由泳..." ); } }); } public static void goSwimming (Swim s) { s.swimming(); } }