第一章 Java基础知识
1.MyEclipse快捷键用法
-
【ALT+ / 】 内容补全 【Ctrl+ / 】 快速添加或取消行注释 【ctrl+shift+/ 】 【ctrl+shift+\】添加/删除<!-- -->注释 【Ctrl+D】 删除当前行 【Ctrl+O】 显示大纲 【Ctrl + M 】 窗口最大化与还原 【Alt + Shift + R】 选中所需要重命名的类 【Ctrl+Shift+O】 快速地导入import 【ALT + SHIFT + Z】 添加try/catch语句块: 包围方式 【ALT + SHIFT + S】+ R 选择Getter and Setter
2.Dos常用命令
-
dir directory 查看当前文件夹下有多少文件及文件夹 cd.. 返回上一层目录 cd. 返回当前目录 md make directory 创建文件夹/目录 rd remove directory 移除文件夹/目录 cd/ 回到磁盘根目录 cd \ 回到磁盘根目录 del 文件名称 删除文件
3.Java语言的特点
-
特点一:面向对象 两个基本概念:类、对象 三大特性:封装、继承、多态 特点二:健壮性 吸收了C/C++语言的优点,但去掉了其影响程序健壮性的部分(如指针、内存的申请与释放等),提供了一个相对安全的内存管理和访问机制 特点三:跨平台性 通过Java语言编写的应用程序在不同的系统平台上都可以运行。
4.变量的定义与命名规则
-
变量:存储数据的空间,计算机会给内存中的每一个变量分配一个内存地址,内存地址不便记忆,我们给变量取名 变量命名规则 变量名由字母、数字、下划线、美元符号$组成 变量名不能使用数字开头,不建议使用美元符号开头 变量名使用小驼峰命名法 变量命名要有意义,见名知意 不能使用Java关键字,Java中大概有50个关键字
5.Java数据类型
-
基本数据类型(8个) 数值型 整型 byte、short、int、long 浮点型 float、double 非数值型 char(字符型)、boolean(true/false) 基本数据类型字节数 byte 1字节 short 2字节 int 4字节 long 8字节 float 4字节 double 8字节 char 2字节 boolean 注意: 在Java中小数默认是double类型,整数默认是int类型 因此 float num = 12.1F; double d = 12.1; char字符类型,char类型的数据类型要使用单引号''括起来,单引号中只能写1个字符 因此 char ch1= '1'; char ch2 = '男' boolean类型:boolean类型的数据只能是true或者false 因此 boolean bool = true; boolean bool2 = false; String类型:字符串类型,使用双引号括起来,双引号中给可以写任意多个字符 因此 String str = "hello world";
6.进制转换
-
十进制转换成二进制 整数部分 将该数字不断除以2直到商为零,然后将余数由下至上依次写出,即可得到该数字的二进制表示 以将数字21转化为二进制为例 21 / 2 = 10....1 10 / 2 =5....0 5 / 2 =2....1 2 / 2 =1...0 1 /2 =0....1 所以结果是10101(从后往前写) 小数部分 利用小数部分乘2,取整数部分,直至小数点后为0 以数字0.625为例 0.625 * 2 = 1...0.25 0.25 *2 =0...0.5 0.5*2=1...0 将取得的数字从上到下写(101) 注意: 整数换成二进制不会产生问题,但小数转换成二进制,有可能永远不会余0,所以就会产生精度问题 例如 0.4永远不能准确换算成二进制 0.4 * 2 =0...0.8 0.8 * 2 =1...0.6 0.6 * 2 =1...0.2 0.2 * 2 =0...0.4(到了这一步又回去到第一步了) 即0110... (这四个二进制数无限循环,循环越多精度越高,只能无限接近0.4,但取不到0.4) 任何小于1并且大于-1的数对1取余,则结果就是小于1并且大于-1的数本身。包括0.4 0.4%1 = 0.4 1.4%1 = 0.3999999999999999 10.00-9.60 = 0.40000000000000036(这是因为9.60无法完全转换为2进制,所以就会导致转换后的数会比9.60小。最终的结果一定会大于0.4的) 10.4 %1 =
7.如何声名变量、给变量赋值和使用变量
-
第一种方式:声名变量、给变量赋值分布写 声明变量:数据类型 变量名; 变量赋值:变量名 = 数据; int age; age=10; 第二种方式:声明变量、给变量赋值一步书写 数据类型 变量名 = 数据; short money = 199;
8. 常量
-
在声明变量前加上final,它就是一个常量 第一种方式:声名变量、给变量赋值分布写 声明变量:final 数据类型 变量名; 变量赋值:变量名 = 数据; final int AGE; AGE=10; 第二种方式:声明变量、给变量赋值一步书写 final 数据类型 变量名 = 数据; final short MONEY = 199; 注意 常量名通常大写 不同字符使用下划线分割 只能被赋值一次,通常定义时即对其初始化
9.运算符的分类
-
赋值运算符 算术运算符 基本算术运算符 加 减 乘 除 取余 自增运算符(++) 自减运算符(--) 复合算术运算符 -= += *= /= %= 关系运算符(等于 不等于 大于 小于 大于等于 小于等于) 这个的结果是布尔类型,true或者false 等于和不等于的优先级小于后面四个的优先级 逻辑运算符(与或非) 这个的结果是布尔类型,true或者false 条件运算符(条件 ? 表达式1 : 表达式2) ----又叫三目运算符 如果条件为真,则执行第一个表达式,否则执行第二个表达式 int num1 = 100; int num2 = 101; int max =num1>num2?num1:num2; System.out.println("最大值:"+max); //输出值是num2 注意: 单目运算符包括 ! ++ --,优先级别高 优先级别最低的是赋值运算符 可以通过()控制表达式的运算顺序,()优先级最高 从右向左结合性的只有赋值运算符、三目运算符和单目运算符 算术运算符 > 关系运算符 > 逻辑运算符
10.&和&&与|和||的异同点
-
相同点 运算规律是一样的,只有当&和&&左右两边同时为true的时候,&和&&的运算结果才为true 不同点 &&有短路功能:当&&左边表达式的结果为false,不再判断&&右边表达式的结果,直接输出&&运算结果 &没有短路功能:不管&左边表达的结果为true还是false,都要先去判断&右边表达式的结果,然后再输出&运算结果 相同点 运算规矩是一样的,只要|和||左右两边有一个为true,|和||的运算结果为true 不同点 ||有短路功能:当||左边表达式的结果为true,不再判断||右边表达式的结果,直接输出||运算结果 |没有短路功能:不管|左边表达式的结果为true还是false,都要去判断|右边表达式的结果,然后再输出|运算结果
11.自动和非自动类型转换规则
-
自动类型转换规则 满足自动类型转换的两个条件 两种类型要兼容(数值类型(整形和浮点型)互相兼容) 目标类型大于源类型 double型大于int型 小容量向大容量转换,称为【自动类型转换】,不同类型的大小排序如下 byte < short (char) < int < long < float < double 例如: double d =10.0+10; 这个可以 int b = 10.0+10; 这个不可以 强制类型转换规则 大容量转换成小容量,要进行 【强制类型转换】 ,要加强制类型转换符,程序才能编译通过,但是在运行阶段可能导致精度损失 例如: int b =(int)10.2; 注意: 如采用+=、*=等缩略形式的运算符,系统会自动强制将运算结果转换为目标变量的类型。 short s1 =1; s1 += 1; //不能写成s1 = s1+1;会报错的
12.Scanner的使用
-
第一步:导入Scanner类(JDK中存在的,只需要导入就可以) import java.util.Scanner 第二步:创建Scanner对象 Scanner input = new Scanner(System.in); 第三步:获得键盘输入的数据 int now =input.nextInt();
练习题
1.实现两个字符型数据的加减乘除运算
//字符会根据ASCII表进行运算
//字符 3 对应的十进制是 51 字符 4 对应的十进制是52
char c1 = '3';
char c2 = '4';
System.out.println(c1+c2) 结果是103(51+52)
System.out.println(c1-c2) 结果是-1(51-52)
System.out.println(c1*c2) 结果是2652 (51*52)
System.out.println(c1/c2) 结果是0(51/52)
//同理
char c2='a';
char c3='b';
//字符a对应的十进制是97,字符b对应的十进制是98
2.条件运算符取最大值或者最小值
int num1 = 100;
int num2 = 101;
int max =num1>num2?num1:num2;
System.out.println("最大值:"+max);
//使用键盘录入两个整数,然后通过条件运算符进行比较,输出输入的两个值里面的最小值
//创建Scanner类对象
Scanner input =new Scanner(System.in);
System.out.println("请输入第一个整数:");
int firstNum=input.nextInt();
System.out.println("请输入第二个整数:");
int secondNum=input.nextInt();
int min =firstNum>secondNum?secondNum:firstNum;
System.out.println("你输入的两个数中最小的数是:"+min);
3.使用条件运算符输出3个数中的最大值
int a = 100;
int b = 90;
int c = 200;
int max =a>b?(a>c?a:c):(b>c?b:c);
System.out.println(max);
4.&和&&运算符
int m = 8;
int n = 9;
int a = 8;
int b = 9;
System.out.println((++m!=n)&&(++m==n)); //当左边是false的时候,又因为它是一个&&运算(短路),所以不会计算右边的
System.out.println(m); //输出是9,不是10
System.out.println((++a!=b)&(++a==b)); //当左边是false的时候,因为它是一个&运算(不短路),所以会计算右边的
System.out.println(a); //输出是10
5.单目运算符先自增还是先赋值
int n= 100;
int m = n++;
System.out.println("n="+n); // n=101
System.out.println("m="+m); // m=100
int k =100;
int h = ++k;
System.out.println("k="+k); //k=101
System.out.println("h="+h); //h=101
/*
++/--在变量前或者变量后,此变量都会自增1或者自减1
++/--修饰的变量参与运算,++/--在变量前或者变量后对结果会有影响
++/--在变量前,先进行自增或者自减运算,然后再参与其它运算
++/--在变量后,先去参与运算,然后再进行自增或者自减运算
*/
6.获得四位数中的各个数字
Scanner input = new Scanner(System.in);
System.out.println("请输入一个四位数");
int count = input.nextInt();
int a1 = count%10; //个位
int a2 = count/10%10; //十位
int a3 = count/100%10; //百位
int a4 = count/1000; //千位
System.out.println("a1="+a1+" a2="+a2+" a3="+a3+" a4="+a4);
7.两个变量值交换(面试题)
//第一种方法:定义临时变量
int num1 =8;
int num2=9;
int temp;
temp = num1;
num1 = num2;
num2 = temp;
System.out.println(num1);
System.out.println(num2);
//第二种方法:利用位运算符实现两个变量值交换
int a = 10;
int b =2;
a = a^b;
b = a^b;
a = a^b;
System.out.println(a);
System.out.println(b);
/*
^表示的是异或 (两个位相同为0,相异为1)
a= 10 = 1010
b= 2 = 0010
a^b = 1000
*/
//第三种方法:利用加减法的操作
int a =1;
int b =2;
a = a+b;
b = a-b;
a = a-b;
System.out.println(a);
System.out.println(b);
第二章 选择结构和循环结构
1.switch选择结构
-
switch(表达式){ case 常量值1: //代码语句; break; case 常量值2: //代码语句; break; case 常量值3: //代码语句; break; ......... default: //代码语句; break; 执行规律: 根据表达式的值,来与case后面的常量值进行匹配 匹配上哪个常量值,就执行该case里面的代码语句,代码语句执行完,执行break,退出整个Switch选择结构,执行Switch选择结构后面的代码 如果表达式的值与所有的case常量值不匹配,则执行default里面的代码语句,执行完执行break语句,退出整个Switch选择结构 注意事项: (1)表达式的值可以是int、short、byte、char、枚举类型、String(JDK7及以后版本可以使用) (2)case常量值不能相同 (3)default语句可以在任意位置,一般来说写在最后,也可以省略不写 (4)break的作用是结束整个Switch选择结构,如果不写,会造成“case穿透”现象,此现象可以合理使用
2.do-while循环
-
do{ 循环操作代码 迭代; }while(循环条件); 执行规律: (1)执行一次初始值代码 (2)先执行一次循环操作和迭代,然后判断循环条件是否满足,如果循环条件满足,则继续执行循环操作和迭代 (3) 如果循环条件不满足,则结束整个do-while循环结构,执行do-while循环结构后面的代码 注意: do-while循环结构,是先执行一次循环操作,再判断循环条件,也就是说do-while循环至少会执行一次循环操作 while循环结构,是先判断循环条件,再根据循环条件的结果决定是否执行循环操作,如果第一次循环条件就不能满足,循环操作就一次都不执行。
3.for循环常见问题
-
1. 初始值一定要声名才能使用,可以在for循环之上声名,也可以在for循环里声名 int i=0; for(; i<10;i++){ System.out.println("这是"+i); } 2.缺少循环条件,会进入死循环 for(int i=0;;i++) System.out.println("这是"+i); 3.缺少迭代部分,如果循环条件满足,会进入死循环.若在循环体中加入迭代部分,可以避免死循环 for(int i=0;i<10;){ System.out.println("这是"+i); i++ } 4.缺少初始值、循环条件、迭代部分,程序进入死循环 for(;;){ System.out.println("这是测试");
4.双层for循环
-
外层循环变化一次,内层循环变量变化一遍 外层循环控制行数,内层循环控制每一行的列数 for (int i = 1; i <= 3; i++) { //输出每一行前面的空格“ ” for (int j = 3-i; j >0; j--) { System.out.print(" "); } //输出每一行的“*” for (int k = 2*i-1; k >0; k--) { System.out.print("*"); } //换行 System.out.println(); } 输出内容 * *** *****
练习题
1.求100以内的所有素数(素数:一个大于1的自然数,除了1和它本身外,不能被其他自然数整除
int j=2;
for(int i=2;i<100;i++){
for(j=2;j<i;j++){
if(i%j == 0){
break;
}
}
//当内层for循环完完全全执行一遍的时候,说明i多于小于i的所有正整数而言,不能整除,即它就是素数
if(i==j)
System.out.println(i);
}
2.判断一天是这一年的第几天
Scanner scanner = new Scanner(System.in);
System.out.print("请输入年: ");
int year= scanner.nextInt();
System.out.print("请输入月: ");
int month= scanner.nextInt();
System.out.print("请输入日: ");
int day= scanner.nextInt();
int result = 0;
if (month > 1) {
result += 31;
}
if (month > 2) {
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
result += 29;
} else {
result += 28;
}
}
if (month > 3) {
result += 31;
}
if (month > 4) {
result += 30;
}
if (month > 5) {
result += 31;
}
if (month > 6) {
result += 30;
}
if (month > 7) {
result += 31;
}
if (month > 8) {
result += 31;
}
if (month > 9) {
result += 30;
}
if (month > 10) {
result += 31;
}
if (month > 11) {
result += 30;
}
System.out.println(result + day);
3.假设有n瓶饮料,喝完3个空瓶可以换一瓶饮料,依次类推,请问总共喝了多少瓶饮料
System.out.println("请输入饮料总瓶数:");
Scanner sc = new Scanner(System.in);
int n =sc.nextInt(); //初始饮料总数
int count =n; //用于计算可以兑换多少瓶
int i=0; //兑换次数
while(true){
if(count<3){
System.out.println ("共喝了"+(n+i)+"瓶");
break;
}qw
count -= 3; //喝3瓶
count++; //兑换1瓶
i++; //兑换次数+1
}
4.求两个整数的最大公约数和最小公倍数
Scanner sc = new Scanner(System.in);
int x = sc.nextInt();
int y =sc.nextInt();
int m = gcd(x,y);
System.out.println("最大公约数是:"+m);
int n = x*y/m;
System.out.println("最小公倍数是:"+n);
//最大公约数
static int gcd(int a,int b){
if(b==0)
return a;
return gcd(b,a%b);
}
第三章 数组
1.一维数组
1.1.数组的定义
-
数组是一个变量,存储多个相同数据类型的一组数据
-
变量、数组和集合的区别 相同点: 都是装数据的容器 不同点: 声明一个变量就是在内存空间划出一块合适的空间,只能存储一个数据 声名一个数组就是在内存空间划出一串连续的空间,可以存储多个相同类型的数据 存储数据的空间,可以存储多个不同类型的数据
1.2.数组基本要素
-
标识符:数组的名称,用于区分不同的数组 数组元素:向数组中存放的数据 元素下标(从0开始) 元素类型:数组元素的数据类型 注意: 数组长度固定不变,避免数组越界
1.3.数组的使用规则
-
1.数组声名 数据类型 数组名[]; 或者 数据类型[] 数组名;(最常用写法) 2.分配空间 数组名 = new 数据类型[数组长度]; 3.赋值 数组名[下标] = 数据; 注意:下标从0开始 例如: char[] c = {'1','2','3','4'}; 注意: 1和2可以合并写 数据类型[] 数组名 = new 数据类型[数组长度];(最常用写法)
1.4.八大基本数据类型数组元素默认值
-
float/dou ble 默认值0.0byte/short/int/long 默认值0
char 默认值" "
boolean 默认值 false
String 默认值 null
1.5.声明数组、分配空间、赋值合并写
-
数据类型[] 数组名 = new 数据类型[]{数据1,数据2,....,数据n}; 或者 数据类型 数组名[] = new 数据类型[]{数据1,数据2,....,数据n};
-
声明数组、分配空间、赋值合并写的简写如下: 数据类型[] 数组名 ={数据1,数据2,....,数据n}; 或者 数据类型 数组名[] ={数据1,数据2,....,数据n};(最常用的一种方法)
-
注意: 这里长度是不用写的,否则会报错
1.6.java.util.Arrays
-
Arrays类:操作数组的工具类,该类中提供了很多操作数据的方法 先导入Arrays包 1.Arrays.sort(数组名); 可以使用Arrays类的sort()方法对数组中的数据进行升序排序 2.Arrays.toString(数组名); 将一个数组转换成一个字符串 int nums = {1,2,3,4,5}; String str = Arrays.toString System.out.println(str); 输出的字符串是 [1,2,3,4,5] 3.Arrays.equals(arr1,arr2) 比较两个数组是否相同,两个元素的个数,内容,元素顺序完全相同为true,否则一个不同就是false 4.Arrays.fill(数组名,数值); 将数组中的所有元素替换成你指定的数值 int nums1 = {11,12,13,14,15}; Arrays.fill(nums1,88); for(int i:nums1) System.out.print(i+" "); 输出 88 88 88 88 88 5.Arrays.copyOf(数组名,数组长度) 将数组进行复制,复制长度由你决定.当复制长度小于原来数组长度,则取数组的前面几个元素。若复制长度大于数组长度,则超过部分是类型数据的默认值 int[] nums2={21,22,23,24,25}; int[] copyNums2 = Arrays.copyOf(nums2,3); for(int i:nums2) System.out.print(i+" "); 输出21 22 23 6.Arrays.binarySearch(数组名,数据) 查询数组中某个数据的下标值,调用此方法之前应该先调用排序方法sort()方法,如果没有调用,不能保证结果的准确性。如果数组中不存在该元素,则会返回的是一个负数 int[] scores = {1, 20, 30, 40, 50}; //在数组scores中查找元素20 int res = Arrays.binarySearch(scores, 20); //打印返回结果 1 System.out.println("res = " + res);
2.二维数组
-
二维数组的创建(方式一) 声名二维数组:数据类型[][] 数组名;或者 数据类型 数组名[][]; 分配空间:数组名 = new 数据类型[长度][长度]; 或者 数组名 = new 数据类型[长度][]; 赋值:数组名[下标][下标] = 数据; 例子: int[][] nums; nums = new int[3][2]; 这是分配了一个3行2列的二维数组 nums[0][0] =10; nums[0][1] =20; nums[1][0] =100; nums[1][1] =200; nums[2][0] =110; nums[2][1] =120; for(int i =0;i<nums.length;i++){ for(int j=0;j<nums[i].length;j++){ System.out.print(nums[i][j]+"\t"); } System.out.println(); } [0][0] 10 [0][1] 20 [1][0] 100 [1][1] 200 [2][0] 110 [2][1] 120 二维数组的创建(方式二) 声明数组、分配空间、赋值合并写: 数据类型[][] 数组名 = new 数据类型[][]{{数据1,数据2,...,数据n},{数据1,数据2,...,数据n},...,{数据1,数据2,...,数据n}}; 或 数据类型 数组名[][] = new 数据类型[][]{{数据1,数据2,...,数据n},{数据1,数据2,...,数据n},...,{数据1,数据2,...,数据n}}; String[][] strs = new String[][]{{"hello"},{"java","c++"},{"html","css","js"}}; 上述声明数组、分配空间、赋值可以简写成: 数据类型[][] 数组名 = {{数据1,数据2,...,数据n},{数据1,数据2,...,数据n},...,{数据1,数据2,...,数据n}}; 或 数据类型 数组名[][] = {{数据1,数据2,...,数据n},{数据1,数据2,...,数据n},...,{数据1,数据2,...,数据n}}; char[][] chars = {{'a','b'},{'c','d','e'},{'f'},{'g','k'}}; 注意: 方式二不要声名数组的长度 二维数组,第一个数代表的是行,第二个数代表的是列。 例如: int[][] arr =new int[3][4] 它表示的是3行4列的数。同int[][] arr={{1,2,3,4},{5,6,7,8},{9,10,11,12}} 这个表示的也是3行4列,这个{1,2,3,4}表示的是1行4列
练习题
1.n个整数,前面各数顺序向后移m个位置,最后m个数变成最前面的m个数
public class Test8 {
public static void main(String[] args) {
Scanner sc =new Scanner(System.in);
System.out.println("请输入有多少个整数");
int n =sc.nextInt();
int[] arr =new int[n];
System.out.println("请输入这些整数");
for(int i=0;i<arr.length;i++){
arr[i] = sc.nextInt();
}
System.out.println("排序前");
for (int i : arr) {
System.out.print(i+" ");
}
System.out.println("请输入向后移多少个位置");
int m =sc.nextInt();
int[] ar =new int[m];
//把最后面要移动的m个数赋给另外一个数组
int m1 =0;
for(int i= arr.length-m ;i<arr.length;i++){
ar[m1]= arr[i] ;
m1++;
}
//把前面arr.length-m个数向后移动m个位置
int count =0;
for(int i=arr.length-m-1;i>=0;i--){
arr[arr.length-1-count] = arr[i];
count++;
}
//把最前面空出来的m个位置,从另外一个数组中获得数组中最后m个数
for(int i=0;i<m;i++){
arr[i] = ar[i];
}
System.out.println("排序后");
for (int i : arr) {
System.out.print(i+" ");
}
}
}
2.键盘上输入 3*4 的整型数组,求出最大值和最小值
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner sc =new Scanner(System.in);
int[][] arr =new int[3][4];
System.out.println("请输入12个数");
//for循环获取键盘上的12个数字
for(int i=0;i<arr.length;i++)
for(int j=0;j<arr[i].length;j++){
arr[i][j] = sc.nextInt();
}
int min = arr[0][0];
int max = arr[0][0];
//双层for循环进行遍历,一一和max、min比较
for(int i=0;i<arr.length;i++){
for(int j=0;j<arr[i].length;j++){
if(arr[i][j]>max){
max =arr[i][j];
}
else if(arr[i][j]<min){
min =arr[i][j];
}
}
}
System.out.println("最大值是:"+max);
System.out.println("最小值是:"+min);
}
}
3.键盘上输入 3*4 的整型数组,将四周的数据清0
import java.util.Scanner;
public class Test6 {
public static void main(String[] args) {
Scanner sc =new Scanner(System.in);
int[][] arr =new int[3][4];
System.out.println("输入12个数");
//从键盘上获取12个数
for(int i=0;i<arr.length;i++){
for(int j=0;j<arr[i].length;j++){
arr[i][j] = sc.nextInt();
}
}
//通过观察这个长方形,得出四周为0的规律
for(int i=0;i<arr.length;i++){
for(int j=0;j<arr[i].length;j++){
if(i==0||j==0||i==2||j==3)
arr[i][j] =0;
}
}
//二维数组遍历输出
for(int i=0;i<arr.length;i++){
for(int j=0;j<arr[i].length;j++){
System.out.print(arr[i][j]+" ");
}
System.out.println();
}
}
}
第四章 面向对象
1.类和对象的定义
-
类:具有相同属性和方法的多个对象的集合 对象:客观存在的一个实体(你能看得见摸得着的物体都可以看成是对象),具有属性和方法 属性:描述的是对象的特征 方法:描述的是对象的功能 综述: 类(学生类)是对象(张三这个具体的学生)的抽象,对象是类的具体,万事万物皆对象(桌子是类,当前这个桌子是对象,空调是类,当前这个空调是对象.....)
2.属性和方法的定义及使用
-
属性的定义语法: 声明属性:数据类型:属性名; 或 数据类型 属性名 = 数据 String type; String color = "白色"; 方法的定义语法: 访问权限修饰符 返回值类型 方法名(数据类型 变量名1,数据类型 变量名2,....数据类型 变量名n,){ //描述方法实现功能的代码 } 访问权限修饰符: public(公共的) protected(受保护的) private(私有的) 默认值(什么都不写) 返回值类型: 方法可以返回给你的结果类型,可以是8大基本类型,也可以是引用数据类型 如果没有返回值,那么写void ()里的称之为参数列表,参数可以没有,也可以有一个或者多个,多个之间使用,隔开,参数的数据类型可以是基本数据类型,也可以是引用类型 {}里的方法实现功能的具体代码 方法的调用规则: 在调用方法的时候,方法有返回值给你,返回给你什么类型的结果,你就要使用对应类型的变量去接受这个返回的结果 在调用有参方法的时候,方法需要什么参数,你就需要传递什么参数,要遵循方法参数列表的定义顺序来传递参数 注意: 对于输出语句:System.out.println();其中println()这个,它也是一个封装的方法,它可以接受 它可以不接受参数,就直接写 System.out.println(); 它也可以接受8大基本数据类型和引用数据类型(使用Object类型进行接受,它是所有类的父类.无论是自己创建的,还是JDk中的类都默认继承Object类), 但它不可以接受一个void类型的返回值,因为它没有定义一个void类型的返回值,也不可能有一个void x;这种类型 public void println(void x) {} 例如:错误示例 public void bark(){ System.out.println("汪汪汪"); } System.out.println(dog1.bark()); 方法的分类: 按照方法是否有返回值,可以将方法分为 有返回值的方法: 有返回值的方法需要使用到return关键字将结果返回 无返回值的方法: 写void表示方法没有返回值 按照方法是否有参数,可以将方法分为: 有参数的方法。简称有参方法 没有参数的方法。简称无参方法 综上所述: 没有返回值的无参方法 没有返回值的有参方法 有返回值的无参方法 有返回值的有参方法
3.封装冒泡
-
对遍历和冒泡算法进行封装 public class ArrayMethod { //定义一个方法实现数组遍历 public void printArray(int[] array){ for (int i : array) { System.out.print(i+" "); } } //定义一个方法实现数组排序 public void sort(int[] array){ for (int i = 0; i < array.length-1; i++) { for (int j = 0; j < array.length-1-i; j++) { if(array[j]>array[j+1]){ int temp = array[j]; array[j]= array[j+1]; array[j+1] = temp; } } } } } 传参进行数组的排序和遍历 public class ArrayMethodTest { public static void main(String[] args) { int[] nums = {11,45,27,89,66,52}; ArrayMethod arrayMethod = new ArrayMethod(); System.out.println("数组排序前:"); arrayMethod.printArray(nums); System.out.println(); System.out.println("数组排序后:"); arrayMethod.sort(nums); arrayMethod.printArray(nums); } } 注意: 不能直接调用其它类中的方法,只能通过对象名.方法名()调用,或者通过类名.方法名()。只有在同一个类中,调用本类的方法,可以直接写方法名。但不能在静态方法中调用非静态方法 方式一 成立 public void bark(){ eat(); System.out.println("汪汪汪"); } public String eat(){ return "狗宝宝好饿"; } 方式二 成立 public void bark(){ eat(); System.out.println("汪汪汪"); } public static String eat(){ return "狗宝宝好饿"; } 方式三 直接编译错误 public static void bark(){ eat(); System.out.println("汪汪汪"); } public String eat(){ return "狗宝宝好饿"; }
4.方法重载
-
在同一个类中,多个方法的方法名相同,参数列表不同,这个现象就是方法重载,与方法的返回值和访问权限无关,只关注两点方法名和参数列表 参数列表不同体现在参数的个数,参数的类型,参数的顺序 注意; 构造方法和普通方法都可以进行重载
5.构造方法
-
每创建一个类,系统会自动给这个类添加一个无参构造方法,并且是隐藏的 (1)构造方法是用来创建对象的 (2)构造方法的名称与类相同 (3)构造方法没有返回值,void也不写 (4)当你在类中自定义了其它有参构造方法后,默认的无参构造方法会被覆盖,所以如果new的一个对象没有参数,需要我们自己添加一个无参构造方法 (5) 谁调用构造方法,this就指向哪个对象 super()调用父类的无参构造方法
-
例子 定义一个方法实现将Dog类数组里的dog对象按照年龄进行升序排序 public class Dog { public String name; public int age; } public class DogMethod { public void sort(Dog[] dog){ //由于Arrays.sort()方法是排列数组中的数字的,而这传过来的则是数组地址,所以需要额外定义一个数组 int[] arr = new int[dog.length]; for (int i = 0; i < dog.length; i++) { arr[i] = dog[i].age; } for (int i = 0; i < arr.length; i++) { Arrays.sort(arr); } for (int j = 0; j < arr.length; j++) { dog[j].age =arr[j]; } } } public class DogMethodTest { public static void main(String[] args) { Dog dog1 = new Dog(); dog1.age = 2; Dog dog2 = new Dog(); dog2.age = 6; Dog dog3 = new Dog(); dog3.age = 1; Dog dog4 = new Dog(); dog4.age = 3; Dog[] d ={dog1,dog2,dog3,dog4}; System.out.println("排序前年龄"); for (int i = 0; i < d.length; i++) { System.out.print(d[i].age+" "); } DogMethod dm =new DogMethod(); dm.sort(d); System.out.println("排序后年龄"); for (int i = 0; i < d.length; i++) { System.out.print(d[i].age+" "); } } }
6.成员变量和局部变量
-
成员变量 定义在类中方法外(循环结构外)的变量 作用域是整个类中 可以不进行初始化,系统会给一个默认值 同一个类中可以有同名的全局变量和局部变量,但使用原则遵循就近原则 局部变量 定义在方法内(循环结构内)的变量 作用域仅限于定于它的结构内 在使用之前要进行初始化,否则程序会报错 同一个方法中不能有同名的局部变量
7.封装
-
将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过类提供的方法来实现对隐藏信息的操作和访问 把尽可能多的东西藏起来,对外提供便捷的接口 如何实现封装 1.修改属性的可见性 设为private,防止错误的修改(在另外一个类中不能直接对 对象的属性做操作) 2.创建共有的getter/setter方法 用于属性的读写 3.在getter/setter方法中加入属性控制语句 对属性的合法性进行判断
-
封装例子 public class Penguin { private String name; //如果前面不写修饰符,则就是默认的修饰符 private int health; private int love; public Penguin(String name, int health, int love) { super(); this.name = name; this.health = health; this.love = love; } public Penguin() { super(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getHealth() { return health; } public void setHealth(int health) { if(health<0){ System.out.println("你的宠物早就挂了,重新复活,默认负值60"); this.health =60; return; //return在方法中的作用有两个,一个是返回结果值,另一个作用是结束方法后续代码的执行 } this.health = health; } public int getLove() { return love; } public void setLove(int love) { if(love<0){ System.out.println("多和你的宠物培养感情,默认提升亲密度到70"); this.love =70; return; } this.love = love; } } public class PenguinTest { public static void main(String[] args) { Penguin p1 =new Penguin(); p1.setName("QQ"); p1.setHealth(-10); p1.setLove(-10); } } 解说 企鹅的健康度不可能是负值,如果对这个健康属性直接进行赋值,极度不合理。所以封装的好处就体现了(不是直接调用属性,而是直接调用方法) 类名只能被public和default修饰。不能使用其它的修饰符进行修饰
8.包
8.1 包的概念
-
作用 1.允许类组成较小的单元(类似文件夹),易于找到和使用相应的文件 2.防止命名冲突,区分名字相同的类 3.有助于实施访问权限控制
-
如何创建包 用package声名包,以分号结尾,位置是在源代码的第一条语句(注释不算)
-
包命名规范 1.包名由小写字母组成,不能以圆点开头或结尾 package mypackage; 2.包名之前最好加上唯一的前缀,通常使用组织倒置的网络域名 package net.javagroup.mypackage; 3.包名后续部分依不同机构内部的规范不同而不同 package net.javagroup.research.powerpoint; (通常是部门名.项目名)
8.2 包的使用方法
-
JDK提供基本包 java.lang:虚拟机自动引入 例如: 输出语句中的System,它是java.lang包下的System类,只不过不要我们手动导入了,虚拟机已经自动引入了 java.util:提供一些实用类 java.io:输入、输出 如何导入包 为了使用不在同一包中的类,需要在Java程序中使用import关键字导入这个类 import 包名. 类名;. import java.util.*; (导入java.util包中所有类 。 ) * 指包中的所有类,只是指util包下的所有类,不包括util包下的其它包 import cn.jtest.classandobject.School; //导入自己创建的包中指定类
8.3 使用包的注意事项
-
1.一个类同时引用了两个来自不同包的同名类,必须通过完整类名来区分 例如 我们需要在同一个类中使用util和sql包下的Date类,则不能使用import的方式进行导入 import java.util.Date; import java.sql.Date; 需要使用完整类名来导入 java.util.Date date = new java.util.Date(); java.sql.Date date2 = new java.sql.Date(123456789L); 2.每个包都是独立的,顶层包不会包含子包的类 例如 java.util包 不包含java.util.logging包下的类 3.package和import的顺序是固定的 1)package必须位于第一行(忽略注释行) 2)只允许有一个package语句 3)第二个顺序是其次是import 4)第三个顺序是类的声明
9.修饰符
9.1 类的访问修饰符
-
类只能被public或者默认修饰符进行修饰 public修饰符 共有访问级别。 可以被任何地方的类进行访问 默认修饰符 包级私有访问级别。 只可以在同一个包中的其它类进行访问。别的包不行
9.2 类成员的访问修饰
-
private 只在同一个类中能够被访问 默认修饰符 在同一个类中和同一个包中可以被访问 protected 在同一个类中和同一个包中和子类中(子类和父类无论在不在同一个包中)可以被访问 public 任何地方都可以被访问
9.3 变量作用域
-
局部变量: 定义在方法内或者其它结构内的变量 成员变量: 定义在类中(其它结构(for、while、switch等)和方法)之外的变量
10.继承(Java只支持单根继承,一个类中只能有一个直接父类)
10.1 子类继承什么?
-
1.继承public和protected修饰的属性和方法,不管子类和父类是否在同一个包中 2.继承默认权限修饰符修饰的属性和方法,但子类和父类必须在同一个包中
10.2 子类不能继承父类什么?
-
private属性和方法 子类与父类不在同包,使用默认访问权限的成员 构造方法
10.3 什么时候使用继承?
-
符合is-a关系的设计使用继承 例如 藏獒是一种狗 狗是一个类,藏獒是狗的具体实例化。边牧也是狗的实例化,所以这两个品种的狗都有狗的特征,所以狗可以是个父类 猫是哺乳动物 猪也是哺乳动物、大象也是哺乳动物。所以可以提取他们的共同点,作为哺乳动物的属性 继承是代码重用的一种方式,将子类共有的属性和行为放到父类中
10.4 super的用法
-
子类访问父类成员 访问父类构造方法 super(); (此语句只能在构造方法中出现) super(参数); (此语句只能在构造方法中出现) 访问父类属性 super.属性名 (出现在子类中的任何地方) 访问父类方法 super.方法名 注意: 1.子类不能够访问父类中定义为private的属性和方法 2.在子类构造方法中,即使不写super(),它也是存在的。如果自己要写的话,必须放在构造方法中的第一行 当在子类构造方法中写了super(有参),则super()会消失. 即他们都必须都要在第一行,所以在同一个子类构造方法中,只能写一个或者不写默认是super(),super()或者super(有参) 3.super只能出现在子类的方法和构造方法中 4.super不能访问父类private成员(方法和变量) 5.在子类中,调用父类构造函数,只能在子类构造函数中调用。 例子(容易忘记隐形的super()) class Car { private int site = 4; //座位数 Car(){ System.out.println ("载客量是"+site+"人"); } public void setSite(int site){ this.site = site; } void print(){ System.out.print("载客量是"+site+"人"); } } class Bus extends Car { Bus(int site){ setSite(site); } } public static void main(String[] args) { Bus bus = new Bus(20); bus.print(); } 输出结果是 载客量是4人 载客量是20人
10.5 static变量的作用
-
1.能被类的所有实例共享,可作为实例之间进行交流的共享数据 2.如果类的所有实例都包含一个相同的常量属性,可把这个属性定义为静态常量类型,从而节省内存空间
10.6 类的成员变量
-
类变量(静态变量) 被static修饰的变量 在内存中只有一个拷贝 类内部,可在任何方法内直接访问静态变量 其他类中,可以直接通过类名访问
静态方法内,可以调用非静态方法(非静态变量)和静态方法(静态变量),而在非静态方法内不可以调用静态方法(有问题)
实例变量
没有被static修饰的变量
每创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响
注意:
局部变量不能加static,包括protected,private,public都不能加
全局变量都可以加
因为:
静态变量和常量是在编译时确定的,而方法的局部变量是在运行时确定的,也就是说方法是可以变的,在方法体不在在被对象所使用的时候,方法体也就被回收了,
可是如果方法中有静态方法就不可以了,因为静态变量是不可以被回收的,他会随着类的存在而一直存在,所以这是矛盾的,所以方法中不可以有静态变量
在实例方法里不可以定义static变量
public void f(){
static int x =0; //这是错误的
}
在静态方法里也不可以定义static变量
public static void f(){
static int x =0; //这是错误的
}
11.重写和多态
11.1 重写
-
什么是方法重写? 子类根据需求对从父类继承的方式进行重新编写 重写时,可以用super.方法的方式来保留父类的方法 构造方法不能被重写
-
方法重写规则 方法名相同 参数列表相同 返回值类相同 子类访问权限不能高于父类 父类的静态方法不能被子类覆盖为非静态方法,父类的非静态方法不能被子类覆盖为静态方法 父类的私有方法不能被子类覆盖 不能抛出比父类方法更多的异常
-
重写与重载的比较 方法重写: 子类中的方法名、参数列表、返回值都要相同。访问修饰符不能比父类严格 方法重载:是在同一个类中,方法名相同,参数列表不相同,与返回值和访问修饰符无关
-
1.子类访问权限不能高于父类的原因 假设一个父类A 拥有的方法 public void setXXX(){} 可以被其他任意对象调用 这个方法被子类B 覆写后 为 void setXXX(){} 即 默认的 访问权限 只能被本包极其子类 所访问 假设 其他包中的对象 C 调用 方法为: get( A a){ a.setXXX(); } 而此时传入的对象为B类对象,假设为b此时b将转型为a但是b中的setXXX() 调用权限 已经被缩小了这将造成错误。 所以,子类对象访问权限≥不能比父类对象访问权限。 2.父类的私有方法不能被子类覆盖 在JAVA中,子类是无法继承父类的private修饰的属性和方法的 例如父类的属性私有,子类调用必须通过public的set和get获取 这有利于在开发过程中保护父类的某种属性和方法不被覆盖 3.不能抛出比父类方法更多的异常 一个修理家电的人,他能够修理冰箱,电脑,洗衣机,电视机。 一个年轻人从他这里学的技术,就只能修理这些家电,或者更少。你不能要求他教出来的徒弟用从他这里学的技术去修理直升飞机。 在java 中,当我们子类要重写父类中的方法,如果父类的方法有异常声明,那么子类重写这个方法时候, 所要声明的异常不应该比父类的大。只能是小等,或者可以没有。原因如下。 假如我们自定意义一个异常: public class MyException extends Exception { public MyException(String message) { } public MyException() { } } 那么我们定义一个父类,它有一个方法,将声明抛出这个异常。我们把它做成抽象的 public abstract class ParentException { public abstract void test() throws MyException; } 那么将有一个类使用到上面这个类 public class ExceptionTest { private ParentException exception; public ExceptionTest(ParentException exception){ this.exception=exception; } public void app(){ try { exception.test(); } catch (MyException e) { e.printStackTrace(); } } } 对于以上的try~catch 我们就直接捕获MyException异常。这是正确的,它在编译的时候是没有问题的。 假如,有一个ParentException的有一个子类 public class ChildException extends ParentException{ public void test() throws Exception{ } } 他的方法可以声明抛出比父类大的异常,假如它是正确的。 那么问题来了。当我们执行ExceptionTest类的时候,我们以这个子类做为对象传进去。 exception.test();这个地方将在运行的时候实际调用的是ChildException这个子类的test()方法, 而它的声明抛 出却是比父类大的异常,这就使得在ExceptionTest类中,捕获异常处出现了毛病, 因为它无法捕获Exception异常。 综上所诉,子类重写父类的方法时候不能声明抛出比父类大的异常
11.2 Object类被子类经常重写的方法
-
toString() Object中的toString()方法,返回当前对象本身的有关信息 例如 Student student =new Student("张三",20); System.out.println(student.toString()); 输出内容:com.bdqn.demo03.Student@2e739136 toString()方法实现 public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } 扩展 System.out.println(对象名); PrintStream.class public void println(Object x) { String s = String.valueOf(x); synchronized (this) { print(s); newLine(); } } String.class public static String valueOf(Object obj) { return (obj == null) ? "null" : obj.toString(); } 解释上段代码作用 第一步 调用println(对象名) 第二步 把对象名传参给String类的valuOf()方法 第三步 根据valueOf()方法里进行判断,若对象是null,则返回null,否则对象会调用toString()方法 (子类继承父类,如果子类调用的方法重写父类了,则调用自己重写的,否则调用父类的)
-
equals() Object中的equals()方法,返回的是两个对象地址值的比较或者两个数值的比较 例如 Student student =new Student("张三",20); Student student1 =new Student("张三",20); System.out.println(student1.equals(student)); 输出内容:false equals()方法实现 public boolean equals(Object obj) { return (this == obj); //this指的是当前对象,即谁调用equasl()方法谁就是this } == 比较引用是比较地址值是否相同 (引用类型包括 数组,类和接口) 比较数值是比较数值是否相同 注: Object类的equals()方法与==没区别 当有特殊需求,如认为属性相同即为同一对象时,需要重写equals() Java.lang.String重写了equals()方法,把equals()方法的判断变为了判断其值
11.3 重写equsla()方法和toString()方法的例子
-
public class Student { private String name; private int age; //父类Objec类中的toString()不能满足Student类的使用,那么就重写Object类中的toString()方法 @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; } //重写Object类中的equals()方法:如果两个对象的name和age值一样,就认为两个对象是同一个对象 @Override public boolean equals(Student stu) { //自己跟自己进行比较 if(this==stu){ return true; } //两个对象进行比较,比较姓名和年龄,姓名是String类型,比较内容,采用String里的equals()方法,比较年龄,采用==比较 if(this.name.equals(stu.name)&&this.age==stu.age){ return true; } return false; } } public class Demo01 { public static void main(String[] args) { //创建两个学生对象 Student stu1 = new Student("张三", 22); Student stu2 = new Student("张三", 22); boolean result2 =stu1.equals(stu2); System.out.println(result2); //true 若不重写则输出false System.out.println(stu1.toString()) //Student [name=张三, age=22] 若不重写输出的是 com.bdqn.demo03.Student@2e739136 即 包名.类名@... /* * Object类里的toString()方法输出的是对象的地址值 * 子类中直接输出对象和通过对象调用toString()方法,都是按照Object类中的toString()方法输出地址值 * 在应用中输出地址值没有具体的意义,所以在子类中都会重写Object类中的toString()方法 * * Object类里的equals()方法比较的对象的地址值 * 子类在进行比较的时候比较地址值没有具体的应用意义,所以在子类中可以重写Object类里的equals()方法实现对象的具体比较 * * 重写的原因:父类方法不满足子类的需求,所以要重写 */ } }
11.4 多态
-
什么是多态 同一个引用类型,使用不同的实例(对象)而执行不同操作(Animal animal 然后子类传参,可以传new Dog()对象等) 例子 (动物类) public class Animal { private String name; private int health; private int love; public Animal() { super(); } public Animal(String name, int health, int love) { super(); this.name = name; this.health = health; this.love = love; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getHealth() { return health; } public void setHealth(int health) { this.health = health; } public int getLove() { return love; } public void setLove(int love) { this.love = love; } // 定义一个输出动物信息的方法 public void print() { System.out.println("动物昵称:" + this.name + ",健康值:" + this.health + ",亲密度:" + this.love); } //定义一个Animal去医院看病的方法 public void toHospital(){ System.out.println("去医院找医生看病....."); } } (猫类) public class Cat extends Animal{ private String color; public Cat(String name, int health, int love, String color) { super(name, health, love); this.color = color; } public Cat() { super(); } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public void print(){ super.print();System.out.println("宠物颜色:"+color); } public void toHospital(){ System.out.println("(猫)打针......"); this.setHealth(90); } } (狗类) public class Dog extends Animal { private String strain; public Dog(String name, int health, int love, String strain) { super(name, health, love); this.strain = strain; } public Dog() { super(); } public String getStrain() { return strain; } public void setStrain(String strain) { this.strain = strain; } public void print(){ super.print(); System.out.println("宠物品种"+strain); } public void toHospital(){ System.out.println("(狗)吃药......"); this.setHealth(88); } } (主人类) public class Master { //带宠物去看病 public void cure(Animal animal){ //打印宠物基本信息 animal.print(); //宠物健康值小于60,看病 if(animal.getHealth()<60){ System.out.println(animal.getName()+"的健康值小于60"); animal.toHospital(); } } } (测试类) public class Test { public static void main(String[] args) { Master master = new Master(); Animal dog =new Dog("翠花",59,90,"拉布拉多"); master.cure(dog); System.out.println("****************"); Animal cat =new Cat("大咪咪",49,95,"大白"); master.cure(cat); } } 解释说明: 狗和猫都需要去医院看病,但他们看病的方式不同,即方法相同,但方法内部不同,所以可以在他们父类中定义一个toHospital()方法,然后让狗类和猫类去重写这个方法,然后最最最重要的就是主人类了,它使用了父类作为基本类型,所以直接在测试类中传对象就可以了 注意1: 方法重写是多态的基础。没有重写就没有多态。 在这个例子中,如果没有Animal类中的toHospital()方法,而直接用多态会报错,因为你Animal中都没有toHospital()方法,你用animal.toHospital()编译一定通不过的 注意2: 在这个例子中,Animal类中的toHospital()方法,其实是没有用的,但又不能不写,所以可以定义成一个抽象方法
11.5 抽象方法
-
抽象方法可以在抽象类中定义,也可以在接口中定义。普通类不行 抽象方法没有方法体 public abstract class Patients{ public abstract void tackMedicine(); } 在抽象类中可以定义普通方法,也可以有抽象方法,也可以没有抽象方法 抽象方法所在的类要声明为抽象类 子类继承抽象父类,必须要重写父类中的所有抽象方法,如果不重写,子类也要定义成抽象类 抽象类不能实例化,实例化没有任何意义 没有抽象构造方法,也没有抽象静态方法。但抽象类中可以有构造方法 对11.4的例子进行修改(变成抽象类,toHospital()变成抽象方法) (动物类) public abstract class Animal { private String name; private int health; private int love; public Animal() { super(); } public Animal(String name, int health, int love) { super(); this.name = name; this.health = health; this.love = love; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getHealth() { return health; } public void setHealth(int health) { this.health = health; } public int getLove() { return love; } public void setLove(int love) { this.love = love; } // 定义一个输出动物信息的方法 public void print() { System.out.println("动物昵称:" + this.name + ",健康值:" + this.health + ",亲密度:" + this.love); } //定义一个Animal去医院看病的方法 public abstract void toHospital(); }
11.6对象类型向上转型和向下转型
-
子类被创建的时候,首先会在内存中创建一个父类对象,然后在父类对象外部放上子类独有的属性,两者合起来形成一个子类的对象,所以子类可以继承父类中所有的属性和方法,包括private修饰的属性和方法 但是子类只是拥有父类private修饰的属性和方法,却不能直接使用它,也就是无法直接访问到它(子类可以通过调用父类的public声明的get方法来获取父类的private属性,但无法访问父类的private方法)。同时子类可以对继承的方法进行重写(@Override),并且新建自己独有的方法。
-
向上转型 class Phone{ public void show(){ System.out.println("我是一个Phone"); } } class ApplePhone extends Phone{ @Override public void show(){ System.out.println("我是一个ApplePhone"); } public void test(){ System.out.println("I am a ApplePhone"); } } public class Test{ public static void main(String[] args) { Apple apple = new ApplePhone(); apple.show(); apple.test(); //报错 } } 注意: 这里用到了向上转型,就是用父类的引用变量去引用子类的实例,这是允许的。 当向上转型之后,父类引用变量可以访问子类中属于父类的属性和方法,但是不能访问子类独有的属性和方法。 例子中由于子类重写了父类的show()方法,所以调用的show()方法是子类的show()方法,输出结果为:“this is a apple”,而调用子类的test()方法则会报错 向下转型 并不是所有的对象都可以向下转型,只有当这个对象原本就是子类对象通过向上转型得到的时候才能够成功转型。 正常转型 Apple apple = new ApplePhone(); ApplePhone applePhone =(ApplePhone)apple; 向下转型有问题的题目 class HuaWeiPhone extends Phone{ @Override public void show() { System.out.println("我是一台华为手机"); } public void test() { System.out.println("I am a HuaWeiPhone"); } } Phone phone = new ApplePhone(); HuaWeiPhone hp = (HuaWeiPhone)phone 上述代码虽然能够编译成功,但是在运行的时候会报错 因为phone对象是由ApplePhone对象向上转型得到的,只能够向下转型成Apple对象,不能够向下转型成HuaWeiPhone 对象。
11.7 接口
-
接口中的成员变量 默认都是public static final,必须显式初始化。即接口中的变量都是静态常量 接口中的方法 默认都是public abstract的,那么也就是说在定义方法的时候只写public或者只写abstract或者只写public abstract (或者都可以可以不写) 接口没有构造方法,不能被实例化 接口中的方法都是抽象方法 一个接口不能实现另一个接口,但可以继承多个其他接口 实现类可以实现多个接口、多个接口之间使用逗号隔开 一个类必须实现接口中的所有抽象方法(implements),除非这个类也是抽象类
11.8 接口练习题
//定义手机抽象类HandSet类
public abstract class HandSet {
private String brand;
private String type;
public HandSet() {
super();
}
public HandSet(String brand, String type) {
super();
this.brand = brand;
this.type = type;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
// 定义发短信的方法
public abstract void sendInfo();
// 定义打电话的方法
public abstract void calll();
public void info() {
System.out.println("这是一款型号为" + this.type + "的" + this.brand + "手机");
}
}
//定义普通手机类
public class CommonHandSet extends HandSet implements PlayWiring{
public CommonHandSet() {
super();
}
public CommonHandSet(String brand, String type) {
super(brand, type);
}
@Override
public void play(String name) {
System.out.println("开始播放音乐《"+name+"》....");
}
@Override
public void sendInfo() {
System.out.println("发送文字信息.....");
}
@Override
public void calll() {
System.out.println("开始语音通话.....");
}
}
//智能手机
public class ApitudeHandSet extends HandSet implements NetWork, PlayWiring,TakePicture {
public ApitudeHandSet() {
super();
}
public ApitudeHandSet(String brand, String type) {
super(brand, type);
}
@Override
public void takePictures() {
System.out.println("咔嚓......拍照成功");
}
@Override
public void play(String name) {
System.out.println("开始播放视频《" + name + "》");
}
@Override
public void networkConn() {
System.out.println("已经启动移动网络......");
}
@Override
public void sendInfo() {
System.out.println("发送带有图片和文字的信息.....");
}
@Override
public void calll() {
System.out.println("开始视频通话.....");
}
}
//上网接口
public interface NetWork {
void networkConn();
}
//照相接口
public interface TakePicture {
void takePictures();
}
//播放接口
public interface PlayWiring {
void play(String name);
}
//测试类
public class Test {
public static void main(String[] args) {
CommonHandSet chs = new CommonHandSet("索尼爱立信", "G502c");
chs.info();
chs.play("热雪");
chs.sendInfo();
chs.calll();
System.out.println("--------------------");
ApitudeHandSet ahs = new ApitudeHandSet("HTC", "I9100");
ahs.info();
ahs.networkConn();
ahs.play("小时代");
ahs.takePictures();
ahs.sendInfo();
ahs.calll();
}
}
12 异常
-
所有异常类的根类是Throwable,它有两个子类是Exception类和Error类 目前学过的异常 ClassCastException 类型转换异常 例子 Animal表示动物类,Dog表示狗类,是动物的子类,Cat表示猫类,是动物的子类。看下面的代码: Animal a1 = new Dog(); // 1 Animal a2 = new Cat(); // 2 Dog d1 = (Dog)a1; // 3 Dog d2 = (Dog)a2; // 4 强制类型转换的 前提是 父类引用指向的对象的类型是子类的时候才可以进行强制类型转换, 如果父类引用指向的对象的类型不是子类的时候将产生java.lang.ClassCastException异常。 而在这里的第四行代码,把a2强制转换陈Dog类,而Dog不是Cat的父类 ,因此这违背了父类引用指向的对象的类型是子类 ArithmeticException 算术异常 例子 public static void main(String[] args){ System.out.println(1/0); System.out.println("程序结束"); //此语句永远不会执行 } ArrayIndexOutOfBoundsException 数组下标越界异常 例子 int arr[] =new int[1]; //一共就只能存储1个数 arr[0] = 10; arr[1] = 20; NullPointerException 空指针异常 例子 public class Test { private String name; public void setName(String name) { this.name = name; } public void print(){ printString(name); } public void printString(String s){ System.out.println(s.length()); } public static void main(String[] args) { Test test = new Test(); test.print(); } }
-
异常语句中的return与exit() 若执行try语句,try中有return,那么finally代码块还是会执行的,它是先执行finally中的语句,然后再返回到try语句中执行最后一行代码return 若执行catch中语句,catch中有return,是同try中一样,finally也会执行 程序只有一种情况不执行finally中的代码块,遇到System.exit(非0数); 结束虚拟机的运行
-
Exception是所有异常类的父类,所有异常产生的对象都可以被Exception类接受。这里的异常对象是ArithmeticException, 当产生异常会把这个异常对象传给e,但这个异常接受的类型一定是Exception或者是ArithmeticException,不可以是别的类型 public static void main(String[] args){ try { System.out.println(1/0); } catch (Exception e) { System.out.println(e); System.out.println("出现了异常"); } finally{ System.out.println("感谢使用本程序"); } } }
-
可以使用多个catch捕获异常,当有多个catch时,catch中的异常Exception一定要放在所有catch的最后,否则编译不通过, Exception是所有异常类的父类,若放在首行,则它一定会执行,而别的异常类写了也多余,所以一定要放在所有catch的最后 异常捕获语句 try{}一定要有,而catch{}和finally{},这两者必须要有其中的一个,或者都有 public class Demo02 { public static void main(String[] args) { try { System.out.println(1/0); } catch (InputMismatchException e) { System.out.println("要求输入的是整数,不能是其它门类型"); e.printStackTrace(); } catch (ArithmeticException e){ System.out.println("除数不能为0"); e.printStackTrace(); }catch (Exception e){ System.out.println("其它异常处理"); }finally { System.out.println("感谢使用本程序!"); } } }
-
创建对象必须要看一下构造方法,看一下这个构造方法需不需要传参 例如 FileInputStream类,JDK中定义它的构造方法都是要传参的,所以在构建这个对象时,都是传参的 File file =new File("D:/a.txt"); FileInputStream fis = new FileInputStream(file);
-
throw抛出异常,需要在此方法上声名(使用throws声名)异常或者在此方法体内用try catch包围抛出的异常。 当在另一个方法体内需要调用这个方法时,需要在另一个方法体上抛出异常或者用try catch包围 例子 public class Student { private String name; private int age; private String gender; public Student() { super(); } public Student(String name, int age, String gender) { super(); this.name = name; this.age = age; this.gender = gender; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) throws Exception { if(age>=1&&age<=100){ this.age = age; }else{ throw new Exception("年龄应该在1-100之间"); } } public String getGender() { return gender; } public void setGender(String gender) throws Exception { //对性别进行判断 if(gender.equals("男")||gender.equals("女")){ this.gender = gender; }else{ throw new Exception("性别只能为男或者女,不能是其它数据"); } } } (测试类) public class StudentTest { public static void main(String[] args) { Student stu = new Student(); try { stu.setGender("东方不败"); } catch (Exception e) { e.printStackTrace(); } try { stu.setAge(120); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(stu.getGender()); System.out.println(stu.getAge()); } }
-
throw与throws区别 throws: 跟在方法声明后面,后面跟的是异常类名 可以跟多个异常类名,用逗号隔开 表示抛出异常,由该方法的调用者来处理 throws表示有出现异常的可能性,并不一定出现这些异常 throw: 用在方法体内,后面跟的是异常类对象名 只能抛出一个异常对象名 表示抛出异常,由该方法体内的语句来处理 throw则是抛出了异常,执行throw一定出现了某种异常 例子(自定义异常) public class Student { private String name; private int age; private String gender; public Student() { super(); } public Student(String name, int age, String gender) { super(); this.name = name; this.age = age; this.gender = gender; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) throws AgeException { if(age>=1&&age<=100){ this.age = age; }else{ throw new AgeException("年龄应该在1-100之间"); //由该方法体内的语句来处理 } } public String getGender() { return gender; } public void setGender(String gender) throws GenderException { //对性别进行判断 if(gender.equals("男")||gender.equals("女")){ this.gender = gender; }else{ throw new GenderException("性别只能为男或者女,不能是其它数据"); //在catch中指定具体的异常类型 (不要直接找Exception) } } } (创建一个年龄异常类) public class AgeException extends Exception { public AgeException(String message){ super(message); } } (创建一个性别异常类) public class AgeException extends Exception { public AgeException(String message){ super(message); } } (测试类) public class StudentTest { public static void main(String[] args) { Student stu = new Student(); try { stu.setGender("东方不败"); //由方法的调用者来处理异常 } catch (Exception e) { e.printStackTrace(); } try { stu.setAge(120); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(stu.getGender()); System.out.println(stu.getAge()); } } 使用自定义异常的步骤 1.定义异常类并且继承Throwable类或者Exception或者RuntimeException 2.编写自定义异常类的构造方法,继承父类的实现 3.实例化自定义异常对象 4.使用throw抛出
13 随机数
-
(int)(Math.random()*n)----->返回大于等于0且小于n的整数 m+(int)(Math.random()*((m+n)-m)----->返回大于等于m且小于m+n(不包括m+n)的随机数