一、Java语言概述
1.1 课程整体内容概述 ![](https://i-blog.csdnimg.cn/blog_migrate/5aa6fbe914f345832065b9262ed3dd0b.png)
1.2 Java语言概述
1.2.1 基础常识
软件:即一系列按照特定顺序组织的计算机数据和指令的集合。分为 系统软件 和 应用软件
人机交互方式:图形化界面 vs 命令行方式
常用DOS命令
应用程序 = 算法 + 数据结构
1.2.2 计算机语言发展迭代史
机器语言--->汇编语言--->高级语言(面向过程和面向对象)
1.2.3 Java版本迭代
1996年,发布JDK1.0
2004年,JDK5.0
2005年,J2SE--->JavaSE, J2EE--->JavaEE, J2ME--->JavaME
2014年,JDK8.0
1.2.4 Java语言应用的领域
> Java Web开发:后台开发
> 大数据开发
> Android应用程序开发:客户端开发
1.2.5 Java语言的特点
> 面向对象性:
两个要素:类、对象
三个特征:封装、继承、多态
> 健壮性:
①去除了C语言中的指针
②自动的垃圾回收机制(仍会出现内存溢出、内存泄漏)
> 跨平台性
1.3 开发环境搭建
1.3.1 JDK、JRE、JVM的关系
1.3.2 JDK的下载和安装
1.3.3 path环境变量的配置
path环境变量:Windows操作系统执行命令时所要搜寻的路径
为什么要配置:java在任何路径下执行成功
1.4 第一个Java程序
1.4.1 Hello World
编写--->编译--->运行
1.4.2 常见的问题解决
14..3 总结第一个程序
1.5 注释与API文档
1.5.1 注释
分类:单行注释、多行注释、文档注释
作用:解释、调试
特点:单行和多行 vs 文档
1.5.2 Java API文档
API: application programming interface 习惯上:将语言提供的类库,都称为API
API 文档:针对提供的类库如何使用,给的一个说明书
1.5.3 良好的编程风格
1.6 EditPlus的使用
1.6.1 开发工具
文本和IDE
1.6.2 EditPlus
二、Java基本语法
2.1 关键字和标识符
2.1.1 java关键字的使用
定义:被 Java 语言赋予了特殊含义,用做专门用途的字符串(单词)
特点:关键字中所有字母都为 小写
2.1.2 保留字
goto、const
2.1.3 标识符的使用
凡是自己可以起名字的地方都叫标识符
涉及的结构:包名、类名、接口名、变量名、方法名、常量名
规则:
规范:
2.2 变量的使用
2.2.1 变量的分类
按数据类型:
按声明的位置 :成员变量、局部变量
2.2.2 定义变量的格式
数据类型 变量名 = 变量值
2.2.3 变量使用的注意点
①变量必须先声明,再去使用
②变量都定义在其作用域内,在作用域内,它是有效的。
③同一个作用域内,不可以声明同名的变量
2.2.3 基本数据变量间的运算法则
自动类型提升:
当容量小的数据类型和容量大的数据类型的变量做运算时,结果自动提升为容量大的数据类型。 byte、char、short--->int-->long--->float--->double
特别的:当byte、char、short三种变量间做运算时,结果为int型
强制类型转换:
1.使用强转符:()
2.注意点:强制类型转换,可能导致精度损失。
2.2.4 String与其他类型
>String属于引用数据类型,翻译为字符串
>声明String数据变量时,必须使用一对双引号""
>String可以和8种基本数据类型变量做运算,且运算只能是连接运算:+
>运算结果任然为String类型
2.3 进制
2.3.1 编程涉及的进制和表示方式
>二进制(binary): 0,1 ,满 2 进 1. 以 0b 或 0B 开头。
>十进制(deciaml): 0-9 ,满 10 进 1 。
>八进制(octal): 0-7 ,满 8 进 1. 以数字 0 开头 表示。
>十六进制(hex): 0-9及A-F ,满 16 进 1. 以 0x 或 0X 开头 表示。此处的 A F 不区分大小写。如:0x21AF +1= 0X21B0
2.3.2 原码、补码、反码
原码 :直接将一个数值换成二进制数。最高位是符号位
负数的反码 :是对原码按位取反,只是最高位(符号位)确定为 1
负数的补码 :其反码加 1
2.3.3 进制间的转换
2.4 运算符
2.4.1 算术运算符:+ - ++ -- * / % +
【典型代码】
//除号:/
int num1 = 12;
int num2 = 5;
int result1 = num1 / num2;
System.out.println(result1);//2
int result2 = num1 / num2 * num2;
System.out.println(result2);//10
double result3 = num1 / num2;
System.out.println(result3);//2.0
double result4 = num1 / num2 + 0.0;//2.0
double result5 = num1 / (num2 + 0.0);//2.4
double result6 = (double)num1 / num2;//2.4
System.out.println(result4);
System.out.println(result5);
System.out.println(result6);
//%:取余运算
//结果符号与被模数的符号相同
//经常被使用来判断能否除尽
int m1 = 12;
int n1 = 5;
System.out.println("m1 % n1 = " + m1 % n1);
int m2 = -12;
int n2 = 5;
System.out.println("m2 % n2 = " + m2 % n2);
int m3 = 12;
int n3 = -5;
System.out.println("m3 % n3 = " + m3 % n3);
int m4 = -12;
int n4 = -5;
System.out.println("m4 % n4 = " + m4 % n4);
//前++:先自增1,后运算
//后++:先运算,后自增1
int a1 = 10;
int b1 = ++a1;
System.out.println("a1 = " + a1 + ", b1 = " + b1);
int a2 = 10;
int b2 = a2++;
System.out.println("a2 = " + a2 + ", b2 = " + b2);
//注意点:
short s1 = 10;
//s1 = s1 + 1;编译失败
//s1 = (short)(s1 + 1);编译成功
s1++;//自增1不会改变本身变量的数据类型
//问题:
byte bb1 = 127;
bb1++;
System.out.println("bb1 = " + bb1);//-128
//前--
//后--
【特别说明】
连接符+:只能使用在String和其他数据类型变量之间的运算
2.4.2 赋值运算符:= += -= *= /= %=
【特别说明】
运算结果不会改变变量本身的数据类型
2.4.3 比较运算符:== != > < >= <= instanceof
【特别说明】
> 比较运算符的结果是boolean类型
> “> < >= <=”只能使用在数值数据类型之间
> “== !=”不仅可以使用在数值类型数据之间,还可以使用在其他引用类型变量之间
Account acct1 = new Account(1000);
Account acct2 = new Account(2000);
boolean b1 = (acct1 == acct2);//比较两个账户是同一个账户。
boolean b2 = (acct1 != acct2);//
2.4.4 逻辑运算符:& && | || ! ^
【典型代码】
class LogicTest
{
public static void main(String[] args)
{
//区分& 和 &&
//相同点1:运算结果相同
//相同点2:当左边符号是true时,二者都执行符号右边的运算
//不同点:当左边符号是false时,&会继续执行右边,&&不会执行右边
boolean b1 = true;
b1 = false;
int num1 = 10;
if(b1 & (num1++ > 0))
{
System.out.println("123");
}
else
{
System.out.println("321");
}
System.out.println("num1 = " + num1);
boolean b2 = true;
b2 = false;
int num2 = 10;
if(b2 && (num2++ > 0))
{
System.out.println("123");
}
else
{
System.out.println("321");
}
System.out.println("num2 = " + num2);
//区分| 和 ||
//相同点1:运算结果相同
//相同点2:当左边符号是false时,二者都执行符号右边的运算
//不同点:当左边符号是true时,|会继续执行右边,||不会执行右边
//开发中推荐使用||
}
}
【特别说明】
> boolean类型变量之间
2.4.5 位运算符:<< >> >>> & | ^ ~
【典型代码】
/*
运算符之五:位运算符(了解)
<< >> >>> & | ^ ~
说明:
1.位运算操作的都是整型的数据
2.<<:在一定范围内,没向左移一位,相当于 *2;
>>:在一定范围内,没向右移一位,相当于 /2;
面试题:最高效方式计算2*8? 2 << 3 或8 << 1
*/
class BitTest
{
public static void main(String[] args)
{
int i = 21;
System.out.println("i << 2 :" + (i<<2));
System.out.println("i << 3 :" + (i<<3));
System.out.println("i << 26 :" + (i<<26));
System.out.println("i << 27 :" + (i<<27));
//练习:交换两个变量的值
int num1 = 10;
int num2 = 20;
System.out.println("num1 = " + num1 + ", num2 = " + num2);
//方式一:定义临时变量
//推荐的方式
int beizi = num1;
num1 = num2;
num2 = beizi;
System.out.println("num1 = " + num1 + ", num2 = " + num2);
//变式二:好处:不用定义临时变量
//弊端:①相加操作可能超出存储范围 ②有局限性:只适用于数值类型
num1 = num1 + num2;
num2 = num1 - num2;
num1 = num1 - num2;
System.out.println("num1 = " + num1 + ", num2 = " + num2);
//方式三:使用位运算符
//局限性:只适用于数值类型
num1 = num1 ^ num2;
num2 = num1 ^ num2;
num1 = num1 ^ num2;
System.out.println("num1 = " + num1 + ", num2 = " + num2);
}
}
2.4.6 三元运算符:
【典型代码】
运算符之六:三元运算符
1.结构:(条件表达式)? 表达式1:表达式2
2.说明
①条件表达式结果位boolean类型
②表达式true,执行1;
表达式false,执行2;
③表达式1和表达式2要求一致的
④三元运算符可以嵌套使用
3.凡是可以使用三元运算符的表达式都可以改写成if-else表达式
反之,不成立。
4.如果程序既可以使用三元运算符又可以使用if-else,使用三元运算符。原因:简洁,效率高
2.5 流程控制
2.5.1 顺序结构:程序从上往下执行
2.5.2 分支结构
【if-else条件判断结构】
分支结构的if-else(条件判断结构)
一、3种结构
第一种
if()
{
}
第二种
if()
{
}
else()
{
}
第三种
if()
{
}
else if()
{
}
...
else
{
}
说明:
1.if-else可以相互嵌套
2.如果if-else结构中的执行语句只有一行时,对应的{}可以省略,但是不建议大家省略
import java.util.Scanner;
class IfTest2
{
public static void main(String[] args)
{
Scanner scanner = new Scanner(System.in);
System.out.println("请输入第一个整数:");
int num1 = scanner.nextInt();
System.out.println("请输入第二个整数:");
int num2 = scanner.nextInt();
System.out.println("请输入第三个整数:");
int num3 = scanner.nextInt();
if(num1 > num2)
{
if(num3 >= num1)
System.out.println(num2 + "," + num1 + "," + num3);
else if(num3 <= num2)
System.out.println(num3 + "," + num2 + "," + num1);
else
System.out.println(num1 + "," + num3 + "," + num2);
}
else
{
if(num3 >= num2)
System.out.println(num1 + "," + num2 + "," + num3);
else if(num3 <= num1)
System.out.println(num3 + "," + num1 + "," + num2);
else
System.out.println(num1 + "," + num3 + "," + num2);
}
//练习1
int x = 4;
int y = 1;
if(x > 2)
{
if(y > 2)
System.out.println(x +y);
//System.out.println("athuigu");
else//就近原则
System.out.println("x is " + x);
}
//练习2:测算狗的年龄
int dogAge= 4;
if(dogAge >= 0 && dogAge <= 2)
{
System.out.println("相当于人的年龄" + dogAge * 10.5);
}
else if(dogAge > 2)
{
System.out.println("相当于人的年龄" + 2 * 10.5 + (dogAge - 2) * 4);
}
else
{
System.out.println("狗狗还没出生");
}
//练习3:如何获取一个随机数:10-99
int value = (int)(Math.random() * 90 + 10);//[0.0-1.0) ---> [0.0,90.0) ---> [10.0,100.0) ---> [10,99]
System.out.println(value);
//公式:[a,b] : (int)(Math.random() * (b - a + 1) + a)
//练习4
System.out.println("请输入你的身高:(cm)");
int height = scanner.nextInt();
System.out.println("请输入你的财富:(千万)");
double wealth = scanner.nextDouble();
/*方式一
System.out.println("是否帅:(true/false)");
boolean isHandsome = scanner.nextBoolean();
if(height >= 180 && wealth >= 1 && isHandsome)
{
System.out.println("我一定要嫁给他!");
}
else if(height >= 180 || wealth >= 1 || isHandsome)
{
System.out.println("嫁吧");
}
else
{
System.out.println("不嫁");
}
*/
//方式二
System.out.println("是否帅:(是/否)");
String isHandsome = scanner.next();
if(height >= 180 && wealth >= 1 && isHandsome.equals("是"))
{
System.out.println("我一定要嫁给他!");
}
else if(height >= 180 || wealth >= 1 || isHandsome.equals("是"))
{
System.out.println("嫁吧");
}
else
{
System.out.println("不嫁");
}
}
}
【 switch case】
分支结构之二:switch-case
1.格式
switch(表达式)
{
case 常量1:
执行语句1;
//break;
...
default:
执行语句n;
//break;
}
2.说明
①根据switch表达式中的值,依次匹配case中的常量,一旦匹配成功,则进入相应case结构中,调用其执行语句。
当调用完执行语句后,仍然继续向下执行其他case结构中的执行语句,直到遇到break关键字或此switch-case
语句末尾结束。
②break,可以使用在switch-case结构中,表示一旦执行到此关键字,就跳出switch-case结构
③switch结构中的表达式,只能是如下的6种数据类型之一:
byte, short, char, int, 枚举类型(JDK5.0新增),String类型(JDK7.0新增)
④case之后只能声明常量,不能声明范围
⑤break关键字是可选的。
⑥default:相当于if-else中的else,
default结构是可选的,而且位置是灵活的。
⑦如果switch-case结构中的执行语句可以合并。
⑧凡是可以使用switch-case的结构,都可以转化为if-else。反之,不成立。
⑨当写分支结构时,既可以使用switch-case同时case取值情况不能太多,又可以使用if-else时,优先使用switch-case。
原因:switch-case执行效率稍高。
import java.util.Scanner;
class SwitchCaseTest
{
public static void main(String[] args)
{
Scanner scan =new Scanner(System.in);
int number = 2;
switch(number)
{
case 0:
System.out.println("zero");
break;
case 1:
System.out.println("one");
break;
case 2:
System.out.println("two");
break;
case 3:
System.out.println("three");
break;
default:
System.out.println("other");
}
//练习1
int score = 78;
switch(score/10)
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
System.out.println("不及格");
break;
case 6:
case 7:
case 8:
case 9:
case 10:
System.out.println("及格");
break;
}
switch(score / 60)
{
case 0:
System.out.println("不及格");
break;
case 1:
System.out.println("及格");
break;
}
//练习2
System.out.println("请输入2019年的month:");
int month = scan.nextInt();
System.out.println("请输入2019年的day:");
int day = scan.nextInt();
int sumDays = 0;//定义一个变量来保存总天数
switch(month)
{
case 12:
sumDays += 30;
case 11:
sumDays += 31;
case 10:
sumDays += 30;
case 9:
sumDays += 31;
case 8:
sumDays += 31;
case 7:
sumDays += 30;
case 6:
sumDays += 31;
case 5:
sumDays += 30;
case 4:
sumDays += 30;
case 3:
sumDays += 28;
case 2:
sumDays += 31;
case 1:
sumDays += day;
}
System.out.println(sumDays);
//练习三
/*System.out.println("请输入年份:");
int year = scan.nextInt();
System.out.println("请输入2019年的month:");
int month = scan.nextInt();
System.out.println("请输入2019年的day:");
int day = scan.nextInt();
int sumDays = 0;//定义一个变量来保存总天数
switch(month)
{
case 12:
sumDays += 30;
case 11:
sumDays += 31;
case 10:
sumDays += 30;
case 9:
sumDays += 31;
case 8:
sumDays += 31;
case 7:
sumDays += 30;
case 6:
sumDays += 31;
case 5:
sumDays += 30;
case 4:
sumDays += 30;
case 3:
//sumDays += 28;
if((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)//判断year是否是闰年
{
sumDays += 29;
}
else
{
sumDays += 28;
}
case 2:
sumDays += 31;
case 1:
sumDays += day;
}
System.out.println(sumDays);*/
}
}
2.5.3 循环结构
【for】
/*
For循环结构的使用
一、循环四要素
①初始化条件
②循环条件(boolean类型)
③循环体
④迭代条件
二、for循环的结构
for(①;②;④)
{
③
}
执行过程:① - ② - ③ - ④ - ② - ③ - ④ - ... - ②
*/
class ForTest
{
public static void main(String[] args)
{
for(int i = 1;i <= 5;i++)
{
System.out.println("Hello World!");
}
//i:在for循环内有效,出了for循环失效
//System.out.println("i");
//练习
int num = 1;
for(System.out.print("a");num <= 3;System.out.print("c"),num++)
{
System.out.print("b");
}
//输出结果:abcbcbc
System.out.println();//换行
//例题:遍历100以内的偶数,输出所有偶数的和,输出偶数的个数
int sum = 0;//记录所有偶数和
int count = 0;//记录偶数的个数
for(int i =1;i <= 100;i++)
{
if(i % 2 ==0)
{
System.out.println(i);
sum += i;
count++;
}
}
System.out.println(sum);
System.out.println(count);
}
}
【while】
/*
while循环的使用
一、循环四要素
①初始化条件
②循环条件(boolean类型)
③循环体
④迭代条件
二、while的循环结构
①;
while(②)
{
③;
④;
}
执行过程:① ② ③ ④ ② ③ ④ ... ②
说明:
1.写while循环不要丢迭代条件。可能导致死循环
2.避免死循环
3.for循环可以和while循环相互转换的!
区别:for循环和while循环的初始化条件部分的作用范围不同。
算法:有限性
*/
class WhileTest
{
public static void main(String[] args)
{
//遍历100以内的偶数
int i = 1;
while(i <= 100)
{
if(i % 2 == 0)
{
System.out.println(i);
}
i++;
}
System.out.println(i);//101
}
}
【do-while】
/*
do-while循环的使用
一、循环四要素
①初始化条件
②循环条件(boolean类型)
③循环体
④迭代条件
二、循环结构
①;
do
{
③;
④;
}
while(②);
执行过程:① ③ ④ ② ③ ④ ... ②
//说明:
1.do-while至少会循环一次循环体!
2.开发种,使用for和while更多一点,较少使用do-while。
*/
class DoWhileTest
{
public static void main(String[] args)
{
//遍历100以内的偶数,并计算所有偶数和及偶数的个数
int num = 1;
int sum = 0;
int count =0;
do
{
if(num % 2 == 0)
{
System.out.println(num);
sum += num;
count++;
}
num++;
}
while (num <= 100);
System.out.println(sum);
System.out.println(count);
}
}
【无限循环】
/*
题目:
从键盘上读入个数不确定的整数,并判断读入的正数和负数的个数,输入为0时结束程序。
说明:
1.不在循环条件部分限制次数的结构:for(;;)或while(true)
2.循环结束的方式
方式一:循环条件部分返回false
方式二:在循环体中出现break
*/
import java.util.Scanner;
class ForWhileTest
{
public static void main(String[] args)
{
Scanner scan = new Scanner(System.in);
int positiveNumber = 0;
int negativeNumber = 0;
while(true)//fot(;;)
{
int number = scan.nextInt();
//判断正负
if(number > 0)
{
positiveNumber++;
}
else if(number < 0)
{
negativeNumber++;
}
else
{
break;//跳出循环
}
}
System.out.println("输入的正数个数为:" + positiveNumber);
System.out.println("输入的负数个数为:" + negativeNumber);
}
}
【嵌套循环】
/*
嵌套循环的使用:
1.嵌套循环:将一个循环结构A声明在另一个循环结构B的循环体中
2.
外层循环:循环结构B
内层循环:循环结构A
3.说明
①内层循环遍历一遍,只相当于外层循环体执行一次
②假设外层循环需要执行m次,内层需要执行n次,内层循环体需要循环m * n次
4.外层循环控制行数、内层循环控制列数
*/
class ForForTest
{
public static void main(String[] args)
{
for(int j = 1;j <= 4;j++)
{
for(int i = 1;i <= 6;i++)
{
System.out.print('*');
}
System.out.println();
}
/*
*
**
***
****
*/
for(int i = 1;i <= 4;i++)//控制行数
{
for(int j = 1;j <= i;j++)//控制列数
{
System.out.print('*');
}
System.out.println();
}
/*
****
***
**
*
*/
for(int i = 1;i <= 4;i++)//控制行数
{
for(int j = 1;j <= 5-i;j++)//控制列数
{
System.out.print('*');
}
System.out.println();
}
/*
*
* *
* * *
* * * *
* * * * *
* * * *
* * *
* *
*
*/
}
}
2.5.4 补充:Scanner类的使用
/*
如何从键盘获取不同类型的变量:需要使用Scanner类
具体实现步骤:
1.导包:import java.utill.Scanner;
2.Scanner的实例化
3.调用Scanner类的相关方法,来获取指定类型的变量
注意:
需要根据相应的方法,来输入指定类型的值,如果输入的数据类型与要求的类型不匹配,会报异常
*/
//1.导包:import java.utill.Scanner;
import java.util.Scanner;
class ScannerTest
{
public static void main(String[] args)
{
//2.Scanner的实例化
Scanner scan = new Scanner(System.in);
//3.调用Scanner类的相关方法,来获取指定类型的变量
System.out.println("请输入你的姓名:");
String name = scan.next();
System.out.println(name);
System.out.println("请输入你的芳龄:");
int age = scan.nextInt();
System.out.println(age);
System.out.println("请输入你的体重:");
double weight = scan.nextDouble();
System.out.println(weight);
System.out.println("你是否相中我了呢?");
boolean islove = scan.nextBoolean();
System.out.println(islove);
//对于char型,Scanner没有提供相关的方法。只能获取一个字符串
System.out.println("请输入你的性别:(男/女)");
String gender = scan.next();
char genferChar = gender.charAt(0);//获取索引为0位置上的字符
}
}
2.5.5 衡量一个功能代码的优劣(优化代码)
如何理解流程控制的练习:流程控制结构的使用 + 算法逻辑
> 正确性
> 可读性
> 健壮性
> 高效率与低存储:时间复杂度、空间复杂度(衡量算法好坏的标准)
/*
100以内所有质数的输出。实现方式一
质数:素数,只能被1和它本身整除的自然数。---> 从2开始到这个数-1开始,都不能被这个数本身整除
最小的质数:2
对PrimeNumber.java 的优化
*/
class PrimeNumber1
{
public static void main(String[] args)
{
boolean isFlag = true;//标识i是否被j除尽,一旦除尽修改其值
int count = 0;//记录质数的个数
//获取当前时间的毫秒数
long start = System.currentTimeMillis();
for(int i = 2;i <= 100000; i++)//遍历100000以内的自然数
{
//for(int j = 2;j < i;j++)//j被i去除
for(int j = 2;j <= Math.sqrt(i);j++)//优化二:对本身是质数的自然数
{
if(i % j == 0)//i被j除尽
{
isFlag = false;
break;//优化一:只对本身非质数有效
}
}
if(isFlag == true)
//System.out.println(i);
count++;
isFlag = true;//重置
}
//获取当前时间的毫秒数
long end = System.currentTimeMillis();
System.out.println("质数的个数为:" + count);
System.out.println("花费的时间为:" + (end - start));//18026 ---> 优化一:1517 ---> 优化二:14
}
}
/*
100以内所有质数的输出。 实现方式二
质数:素数,只能被1和它本身整除的自然数。---> 从2开始到这个数-1开始,都不能被这个数本身整除
最小的质数:2
对PrimeNumber.java 的优化
*/
class PrimeNumber2
{
public static void main(String[] args)
{
int count = 0;//记录质数的个数
//获取当前时间的毫秒数
long start = System.currentTimeMillis();
label:for(int i = 2;i <= 100000; i++)//遍历100000以内的自然数
{
for(int j = 2;j <= Math.sqrt(i);j++)
{
if(i % j == 0)//i被j除尽
{
continue label;
}
}
count++;//执行到此步骤的都是质数
}
//获取当前时间的毫秒数
long end = System.currentTimeMillis();
System.out.println("质数的个数为:" + count);
System.out.println("花费的时间为:" + (end - start));
}
}
2.5.6 break和continue关键字的使用
/*
break和continue关键字的使用
使用范围 循环中使用的作用(不同点) 相同点
break: switch-case
循环结构中 结束当前循环 关键字后面不能声明执行语句
continue: 循环结构中 结束当次循环 关键字后面不能声明执行语句
*/
class BreakContinueTest
{
public static void main(String[] args)
{
/*for(int i = 1;i <= 10; i++)
{
if(i % 4 = 0)
{
//break;//123
continue;//123567910
}
System.out.print(i);
}*/
System.out.println("\n");
//************************
label:for(int i = 1;i <= 4; i++)
{
for(int j = 1;j <= 10;j++)
{
if(j % 4 == 0)
{
//break;//默认跳出包裹此关键字的最近一层循环
//continue;
//break lable;//结束指定标识的一层循环结构
continue label;//结束指定标识的一层结构的当次循环
}
System.out.print(j);
}
System.out.println();
}
}
}
三、数组
3.1 数组的概述
3.1.1 数组的理解
Array是多个相同类型数据按 一定顺序排列的集合并使用 一个名字命名并通过编号的方式这些数据进行统一管理。
3.1.2 相关的概念
>数组名
>元素
>角标、下标 、索引
>数组的长度:元素的个数
3.1.3 数组的特点
1)数组是有序排列的
2)数组属于引用数据类型的变量。数组的元素既可以是基本数据类型也可以是引用数据类型
3)创建数组对象会在内存中开辟一整块连续的空间
4)数组的长度一旦确定,就不能修改
3.1.4 数组的分类
① 维数:一维、二维
② 数组元素的类型,基本数据类型的元素的数组、引用数据类型的元素
【数据结构】
1. 数据与数据之间的逻辑关系:集合、一对一、一对多、多对多
2. 数据的存储结构
线性表:顺序表(如:数组)、链表、栈、队列
树形结构:二叉树
图形结构:
3. 算法:排序算法、 搜索算法
3.2 一维数组
public class ArrayTest {
public static void main(String[] args) {
//1 一维数组的声明和初始化
int num;//声明
num = 10;//初始化
int id = 1001;//声明加初始化
//1.1静态初始化:数组的初始化和数组的元素操作同步进行
int[] ids;//声明
ids = new int[]{1001,1002,1003,1004};
//1.2动态初始化:数组的初始化和数组的元素操作分开进行
String[] names = new String[5];
//也是正确的写法
int[] arr4 = {1,2,3,4,5};//类型推断
//总结:数组一旦初始化完成,其长度就确定了。
//2.如何调用数组的指定位置的元素:通过角标
//数组的角标从0开始,到数组的长度-1结束
names[0] = "wangming";
names[1] = "wanghe";
names[2] = "zhangxueliang";
names[3] = "sunjulong";
names[4] = "wanghongzhi";//charAt(0)
// names[5] = "zhouyang";超出角标范围
//3.如何获取数组的长度
//属性:length
System.out.println(names.length);//5
System.out.println(ids.length);//4
//4.如何遍历数组元素
/*System.out.println(names[0]);
System.out.println(names[1]);
System.out.println(names[2]);
System.out.println(names[3]);
System.out.println(names[4]);*/
for(int i = 0;i < names.length;i++){
System.out.println(names[i]);
}
}
}
package nianwuyin.java;
/*
* ⑤ 数组元素的默认初始化值
* >数组元素是整型:0
* >数组元素是浮点型:0.0
* >数组元素是char型:ASCII = 0 != '0'
* >数组元素是boolean型:false(0)
* >数组元素是引用型数据类型:null != "null"
*
* ⑥ 数组的内存解析(难点)
*/
public class ArrayTest1 {
public static void main(String[] args) {
//5.数组元素的默认初始化值
int[] arr = new int[4];
for(int i = 0;i < arr.length;i++){
System.out.println(arr[i]);
}
System.out.println("****************");
short[] arr1 = new short[4];
for(int i = 0;i < arr1.length;i++){
System.out.println(arr1[i]);
}
System.out.println("****************");
float[] arr2 = new float[5];
for(int i = 0;i < arr2.length;i++){
System.out.println(arr2[i]);
}
System.out.println("****************");
char[] arr3 = new char[5];
for(int i = 0;i < arr3.length;i++){
System.out.println("----" + arr3[i] + "****");
}
System.out.println("****************");
boolean[] arr4 = new boolean[5];
for(int i = 0;i < arr4.length;i++){
System.out.println("----" + arr4[i] + "****");
}
System.out.println("****************");
String[] arr5 = new String[5];
for(int i = 0;i < arr5.length;i++){
System.out.println("----" + arr5[i] + "****");
}
System.out.println("****************");
}
}
3.3 二维数组
package nianwuyin.java;
/*
* 二维数组的使用
*
* 1.理解
* 对于二维数组的理解,我们可以看成是一维数组array1又作为另一个一维数组array2的元素而存在。
* 其实,从数组底层的运行机制来看,其实是没有多维数组的。
*
* 2.二维数组的使用
* ① 二维数组的声明和初始化
* ② 如何调用数组的指定位置元素
* ③ 如何获取数组的长度
* ④ 如何遍历数组
* ⑤ 数组元素的默认初始化值
* ⑥ 数组的内存解析
*/
public class ArrayTest2 {
public static void main(String[] args) {
//1.二维数组的声明和初始化
int[] arr = new int[]{1,2,3};//一维数组
//静态初始化
int[][] arr1 = new int[][]{{1,2,3},{4,5},{6,7,8}};
//动态初始化1
String[][] arr2 = new String[3][2];
//动态初始化2
String[][] arr3 = new String[3][];
//错误的情况
// String[][] arr4 = new String[][4];
// String[4][3] arr5 = new String[][];
// int[][] arr6 = new int[4][3]{{1,2,3},{4,5},{6,7,8}};
//也是正确的写法
int[] arr4[] = new int[][]{{1,2,3},{4,5,9,10},{6,7,8}};
int[] arr5[] = {{1,2,3},{4,5},{6,7,8}};
//2.如何调用数组的指定位置元素
System.out.println(arr1[0][1]);//2
System.out.println(arr2[1][1]);//null
arr3[1] = new String[4];
System.out.println(arr3[1][0]);
//3.如何获取数组的长度
System.out.println(arr4.length);//3
System.out.println(arr4[0].length);//3
System.out.println(arr4[1].length);//4
//4.如何遍历数组
for(int i = 0;i < arr4.length;i++){
for(int j = 0;j < arr4[i].length;j++){
System.out.print(arr4[i][j] + " ");
}
System.out.println();
}
//5.数组元素的默认初始化值
//规定:二维数组分为外层数组的元素,内层数组的元素
//针对初始化方式一:
// 外层元素的初始化值为:地址值
// 内层元素的初始化值为:与一维数组初始化情况相同
int[][] arr6 = new int[4][3];
System.out.println(arr6[0]);//地址值:[I@15db9742
System.out.println(arr6[0][0]);//0
System.out.println(arr6);//[[I@6d06d69c
float[][] arr7 = new float[4][3];
System.out.println(arr7[0]);//地址值
System.out.println(arr7[0][0]);//0.0
String[][] arr8 = new String[4][3];
System.out.println(arr8[1]);//地址值
System.out.println(arr8[1][1]);//null
//针对初始化方式二:
// 外层元素的初始化值为:null
// 内层元素的初始化值为:不能调用,否则报错
String[][] arr9 = new String[4][];
System.out.println(arr9[1]);//null
double[][] arr10 = new double[4][];
System.out.println(arr10[1]);//null
System.out.println(arr10[1][0]);//报错
//6.数组的内存解析
}
}
3.4 数组的常见算法
3.4.1 数组的创建与元素赋值
杨辉三角(二维数组)、回形树(二维数组)、6个数,1-30之间随机生成且不重复
3.4.2 针对数值型的数组
最大值、最小值、总和、平均数等
3.4.3 数组的赋值、复制、反转、查找
package nianwuyin.java;
/*
* 算法的考察:数组的赋值、反转、查找
*/
public class ArrayTest2 {
public static void main(String[] args) {
String[] arr = new String[]{"aa","bb","cc","dd","ee","ff"};
//数组的复制(区别数组变量的赋值)
String[] arr1 = new String[arr.length];
for(int i = 0;i < arr1.length;i++){
arr1[i] = arr[i];
}
//数组的反转
//方法一
// for(int i = 0;i < arr.length / 2;i++){
// String temp = arr[i];
// arr[i] = arr[arr.length - i - 1];
// arr[arr.length - i - 1] = temp;
// }
//方法二
// for(int i = 0,j = arr.length - 1;i < j;i++,j--){
// String temp = arr[i];
// arr[i] = arr[j];
// arr[j] = temp;
// }
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i] + "\t");
}
System.out.println();
//查找
//线性查找:
String dest = "bb";
boolean isFlag = true;
for(int i = 0;i < arr.length;i++){
if(dest.equals(arr[i])){
System.out.println("找到了指定的元素,位置为:" + i);
isFlag = false;
break;
}
}
if(isFlag){
System.out.println("sorry");
}
//二分法查找:(熟悉)
//前提:所要查找的数组必须有序
int[] arr2 = new int[]{-98,-34,2,34,54,66,79,105,210,333};
int dest1 = -34;
int head = 0;//初始的首索引
int end = arr2.length - 1;//初始的末索引
boolean isFlag1 = true;
while(head <= end){
int middle = (head + end)/2;
if(dest1 == arr2[middle]){
System.out.println("找到了指定的元素,位置为:" + middle);
isFlag1 = false;
break;
}else if(arr2[middle] > dest1){
end = middle - 1;
}else{
head = middle + 1;
}
}
if(isFlag1)
{
System.out.println("sorry");
}
}
}
3.4.4 数组的排序算法
【理解】
1)衡量排序算法优劣:时间复杂度、空间复杂度、稳定性
2)排序的分类:内部排序与外部排序
3)不同排序方法的时间复杂度
4)手写冒泡排序
package nianwuyin.java;
/*
* 数组的冒泡排序的实现
*/
public class BubbleSortTest {
public static void main(String[] args){
int[] arr = new int[]{43,42,76,-98,0,64,33,-21,32,99};
//冒泡排序
for(int i = 0;i < arr.length - 1;i++){
for(int j = 0;j < arr.length - 1 - i;j++){
if(arr[j] > arr[j + 1]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
for(int i = 0;i < arr.length;i++){
System.out.print(arr[i] + "\t");
}
}
}
3.4.5 Arrays工具类的使用及数组常见异常
1)理解:Arrays定义在java.util包下
package nianwuyin.java;
import java.util.Arrays;
/*
* java.util.Arrays:操作数组的工具类,里面定义了很多操作数组的方法
*/
public class ArraysTest {
public static void main(String[] args) {
//1.boolean equals(int[] a,int[] b):判断两个数组是否相等。
int[] arr1 = new int[]{1,2,3,4};
int[] arr2 = new int[]{1,3,2,4};
boolean isEquals = Arrays.equals(arr1, arr2);
System.out.println(isEquals);
//2.String toString(int[] a):输出数组信息
System.out.println(Arrays.toString(arr1));
//3.void fill(int[] a,int val):将指定值填充到数组之中。
Arrays.fill(arr1, 10);
System.out.println(Arrays.toString(arr1));
//4.void sort(int[] a):排序
Arrays.sort(arr2);
System.out.println(Arrays.toString(arr2));
//5.int binarySearch(int[] a,int key)
int[] arr3 = new int[]{-98,-34,2,34,54,66,79,105,210,333};
int index = Arrays.binarySearch(arr3, 211);
if(index >= 0){
System.out.println(index);
}else{
System.out.println("未找到!");
}
}
}
2)数组常见异常
package nianwuyin.java;
/*
* 数组中的常见异常
* 1.数组角标越界的异常:ArrayIndexOutOfBoundException
*
* 2.空指针异常:NullPointerException
*/
public class ArrayExceptionTest {
public static void main(String[] args) {
//1.数组角标越界的异常:ArrayIndexOutOfBoundException
// int[] arr = new int[]{1,2,3,4,5};
// for(int i = 0;i <= arr.length;i++){
// System.out.print(arr[i]);
// }
//
// System.out.println(arr[-2]);
//2.空指针异常:NullPointerException
//情况一:
// int[] arr1 = new int[]{1,2,3};
// arr1 = null;
// System.out.println(arr1[0]);
//情况二:
// int[][] arr2 = new int[4][];
// System.out.println(arr2[0][0]);
//情况三:
String[] arr3 = new String[]{"aa","bb","cc"};
arr3[0] = null;
System.out.println(arr3[0].toString());
}
}
四、面向对象(上)
4.1 类与对象
4.1.1 面向对象的三条主线
1)Java类及类的成员:属性、方法、构造器;代码块、内部类
2)面向对象的三大特征:封装性、继承性、多态性、(抽象性)
3)其他关键字:this\supper\static\final\abstract\interface\package\import等
“大处着眼、小处着手”
4.1.2 面向对象与面向过程的理解
面向过程:强调是功能行为,以函数为最小单位,考虑怎么做。
面向对象:强调具备了功能的对象,以类/对象为最小单位,考虑谁来做
4.1.3 完成一个项目或功能的思路 ![](https://i-blog.csdnimg.cn/blog_migrate/b6a061d0e20c4a5afcda5a151e5c2247.png)
4.1.4 面向对象中两个重要的概念
类:对一类事物的描述,是抽象的、概念上的定义
对象:实际存在的该类事物的每个个体,因而也称为实例(instance)
>面向对象程序设计的重点是类的设计
>类的设计,其实就是类的成员的设计
二者的关系:对象,是类new'出来的,派生出来的
4.1.5 面向对象思想落地实现的规则
1.创建类,设计类的成员
2.创建类的对象
3.通过“对象.属性”或“对象.方法”调用对象的结构
【概念补充】
属性 == 成员变量 == field == 域、字段
方法 == 成员方法 == 函数 == method
创建类的对象 == 类的实例化 == 实例化
4.1.6 对象的创建与对象的内存解析
如果创建了一个类多个对象,则每个对象都独立的拥有一套类的属性。(非static)意味着:如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值
4.1.7 匿名对象
理解,我们创建的对象,没有显式地赋给一个变量,即为匿名对象
特征:匿名对象只能调用一次
使用:
public class InstanceTest {
public static void main(String[] args) {
Phone p = new Phone();
// p = null
System.out.println(p);// 含变量类型的地址值
p.sendEmail();
p.playGame();
//匿名对象
new Phone().sendEmail();
new Phone().playGame();
new Phone().price = 1999;
new Phone().showPrice();//0.0
//********************
PhoneMall mall = new PhoneMall();
//mall.show(p);
//匿名对象的使用
mall.show(new Phone());
}
}
class PhoneMall{
public void show(Phone phone){
phone.sendEmail();
phone.playGame();
}
}
class Phone{
double price;//价格
public void sendEmail(){
System.out.println("发送邮件");
}
public void playGame(){
System.out.println("打游戏");
}
public void showPrice(){
System.out.println("价格为: " + price);
}
}
4.1.8 JVM内存结构
编译源程序后,生成一个或多个字节码文件。
我们使用JVM中的类的加载器和解释器对生成的字节码文件进行解释运行。意味着,需要将字节码文件对应的类加载到内存中,设计到内存解析。
《JVM规范》
虚拟机栈,即为平时提到的栈结构。我们将局部遍历存储在栈结构中
堆,将new出来的结构(数组、对象)加载在堆空间中。对象的属性(非static)加载在堆空间中
方法区:类的加载信息、常量池、静态域
4.2 类的结构:属性(成员变量)
4.2.1 成员变量 vs 局部变量
* 类中属性的使用
*
* 属性:(成员变量) vs 局部变量
* 1.相同点
* 1.1定义变量的格式: 数据类型 变量名 = 变量值;
* 1.2先声明后使用
* 1.3变量都有其对应的作用域
*
*
* 2.不同点
* 2.1在类中声明的位置不同
* 属性:直接定义在类的一对{}内
* 局部变量:将声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
* 2.2关于权限修饰符的不同
* 属性:可以在声明属性时,指明其权限,使用权限修饰符。
* 常用的权限修饰符:private、public、缺省、protected --->封装性
* 目前:大家声明属性时,使用缺省就可以了
* 局部变量:不可以使用权限修饰符
* 2.3默认初始化值的情况
* 属性:类的属性,根据其类型,都有默认初始化值
* 整型(byte\short\int\long):0
* 浮点型(float\double):0.0
* 字节型(char):0 '\u0000'
* 布尔型(boolean):false
* 引用数据类型(类、数组、接口):null
*
* 局部变量:没有默认初始化值
* 意味着,我们在调用局部变量时,一定要显示赋值。
* 特别的,形参在调用时赋值即可
*
* 2.4在内存中加载的位置不同
* 属性:加载到堆空间(非static)
* 局部变量:加载到栈空间
4.2.2 回顾变量的分类:
方式一:按照数据类型
方式二:按照在类中声明的位置
4.3 类的结构:方法
4.3.1 方法的使用(return)
* 类中方法的使用
*
* 方法:描述类应该具有的功能
* 比如Math类:sqrt()\random
* Scanner类:nextXxx()
* Arrays类:sort()\binarySearch()\toString()\equals() \
*
* 1.举例
* public void eat(){}
* public void sleep(int hour){}
* public String getName(){}
* public String getNation(String nation){}
*
* 2.方法的声明:权限修饰符 返回值类型 方法名(形参列表){
* 方法体
* }
* 注意:static\final\abstract 来修饰方法 ,后面再讲。
*
* 3.说明
* 3.1 关于权限修饰符:默认方法的权限修饰符先都使用public
* java中的4种权限修饰符:private\public\缺省、protected ---封装性再细说
* 3.2返回值类型,有返回值 vs 没有返回值
* 3.2.1 如果方法有返回值,则必须在方法声明时,指定返回值的类型。同时,方法中,需要使用
* return关键字来返回指定类型的变量或常量(数据)
* 如果方法没有返回值,则声明时,使用void来表示,通常没有返回值的方法中,就不需要return,
* 但是,如果使用的化,只能“return”表示结束此方法的意思。
* 3.2.2 我们定义方法该不该有返回值?
* ①题目要求
* ②凭经验
* 3.3 方法名:属于标识符,遵循规则和规范,“见名知意”
* 3.4 形参列表:方法可以声明0个1个或多个形参
* 3.4.1格式:数据类型1 形参1,数据类型2 形参2,...
* 3.4.2定义方法时要不要形参?
* ①题目要求
* ②凭经验,具体问题具体分析
* 3.5 方法体:方法功能的体现
*
*4.return关键字的使用说明:
* 1.使用范围:使用在方法中
* 2.作用:
* ①结束方法
* ②针对有返回值类型的方法,使用return 数据方法返回所要的数据。
* 3.注意点:return后面不可以声明执行语句。
*
*5.方法的使用中
* ①可以调用当前类的属性或方法,特殊的,方法A中又调用了方法A,递归调用
* ②方法中不可以定义方法
4.3.2 方法的重载
* 方法的重载(overload)
* 1.定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
*
* “两同一不同”:同一类、相同方法名
* 参数列表、参数个数不同,参数类型不同
* 2.举例
* Arrays类中重载的sort/binarySearch
*
* 3.判断是否重载:
* 跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系!
*
* 4.在通过对象调用方法时,如何确定某一个指定的方法:
* 方法名 ---> 参数列表
面试题:方法的重载与重写的区别?
4.2.3 可变个数形参的方法
package nianwuyin.java1;
/*
* 可变个数的方法
* 1.JDK5.0 新增的内容
* 2.具体使用:
* 2.1 可变个数形参的格式:数据类型 ... 变量名
* 2.2 当调用可变形参的方法时,传入参数的个数可以是0个,1个,2个...
* 2.3 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
* 2.4 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载。二者不共存
* 2.5 可变个数形参在方法的形参中,必须声明在末尾
* 2.6 可变个数形参在方法的形参中,最后只能声明一个可变形参。
*/
public class MethodArgsTest {
public static void main(String[] args) {
MethodArgsTest test = new MethodArgsTest();
test.show(12);
test.show("hello");
// test.show("hello","world");
// test.show();
test.show(new String[]{"aa", "bb","cc"});
}
public void show(int i){
}
public void show(String s){
System.out.println("show(string)");
}
public void show(String ... strs){
System.out.println("String ... strs");
for(int i = 0;i < strs.length;i++){
System.out.println(strs[i]);
}
}
// public void show(String[] strs){
//
// }
//The variable argument type String of the method show must be the last parameter
// public void show(String ... strs, int i){
//
// }
}
4.2.4 Java的值传递机制
1)方法内的变量赋值:如果变量是基本数据类型,此时赋值的是变量所保存的数据值。如果是变量引用数据类型,此时赋值的是变量所保存的数据的地址值。
2)针对于方法的参数概念:形参:方法定义时,声明的小括号内的参数。实参:方法调用时,实际传递给形参的数据
3)值传递机制: 如果参数是基本数据类型,此时实参赋给形参的是实参真实储存的数据值;如果参数是基本数据类型,此时实参赋给形参的是实参储存数据的地址值
Eclipse的使用
4)典型例题与内存解析:
public class ValueTransferTest2 {
public static void main(String[] args) {
Data data = new Data();
data.m = 10;
data.n = 20;
System.out.println("m = " + data.m + ",n = " + data.n);
//交换m和n的值
ValueTransferTest2 test = new ValueTransferTest2();
test.swap(data);
System.out.println("m = " + data.m + ",n = " + data.n);
}
public void swap(Data data){
int temp = data.m;
data.m = data.n;
data.n = temp;
}
}
class Data{
int m;
int n;
}
4.2.5 递归方法
package nianwuyin.java2;
/*
* 递归方法的使用(了解)
* 1.递归方法:一个方法体内调用自己
* 2.方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制
* 3.递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环
*/
public class RecursionTest {
public static void main(String[] args) {
//例1 计算1-100所有自然数和
//方式一
int sum = 0;
for(int i = 1;i <= 100;i++){
sum += i;
}
System.out.println(sum);
//方式二,递归
RecursionTest test = new RecursionTest();
int sum1 = test.getSum(100);
System.out.println(sum1);
// 例3 已知有一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),其中n是大于0的整数,求f(10)的值
int sum3 = test.f(10);
System.out.println(sum3);
}
//例1 计算1-100所有自然数和
public int getSum(int n){
if(n == 1){
return 1;
}else{
return n + getSum(n - 1);
}
}
//例2 计算1-100所有自然数乘积
public int getSum1(int n){
if(n == 1){
return 1;
}else{
return n * getSum(n - 1);
}
}
//例3 已知有一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),其中n是大于0的整数,求f(10)的值
public int f(int n){
if(n == 0){
return 1;
}else if(n == 1){
return 4;
}else{
return f(n - 1) + 2*f(n - 2);
}
}
//例4 斐波那契数列
//例5 汉诺塔
//例6 快排
}
4.4 面向对象的特征一:封装性
4.4.1 为什么引入封装性
我们程序设计追求“高内聚,低耦合”。高内聚 :类的内部数据操作细节自己完成,不允许外部干涉; 低耦合 :仅对外暴露少量的方法用于使用。
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。
4.4.2 问题引入
* 当我们创建一个类的对象以后,我们可以通过“对象.属性”的方式,对对象的属性进行赋值。这里,赋值操作要受到
* 属性的数据类型和存储范围的制约。但是除此之外,没有其他制约条件,但是实际问题中,我们需要加入额外的制约
* 条件。这个条件我们不能在属性声明中体现,我们只能通过方法进行限制条件的添加。(如setLegs)
* 同时避免用户再使用“对象.属性”的方式进行赋值。则需要将属性声明为private
* --->此时,针对属性就体现了封装性。
4.4.3 封装性思想的具体代码体现
体现一:我们将类的属性xxx私有化(private),同时,提供公共(public)的方法来获取(getXxx)和设置(setXxx)此属性的值。
体现二:不对外暴露的私有的方法
体现三:单例模式(将构造器私有化)
体现四:如果不希望类在包外调用,可以设置为缺省
4.4.4 Java规定的4种权限
顺序:private < 缺省 < protected < public
具体修饰范围:4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类;修饰类的话,只能使用,缺省、public
![](https://i-blog.csdnimg.cn/blog_migrate/8e1dbbed20e971dcfe29393ee968968e.png)
4.5 类的结构:构造器
package nianwuyin.java1;
/*
* 类的结构之三:构造器(或构造方法、constructor)的使用
*
* 一、构造器的作用:
* 1.创建对象
* 2.初始化对象的属性
*
* 二、说明
* 1.如果没有显式的定义类的构造器的话,则系统默认提供一个空参的构造器
* 2.定义构造器的格式:权限修饰符 类名(形参列表){}
* 3.一个类中定义的多个构造器。彼此构成重载
* 4.一旦我们显式地定义了类的构造器,系统不再提供默认的空参构造器
* 5.一个类中,至少有一个构造器
*
*/
public class PersonTest {
public static void main(String[] args) {
//创建类的对象:new + 构造器
Person p = new Person();
p.eat();
Person p1 = new Person("Tom");
System.out.println(p1.name);
}
}
class Person{
String name;
int age;
//构造器
public Person(){
System.out.println("Person()......");
}
public Person(String n){
name = n;
}
public Person(String n, int a){
name = n;
age = a;
}
public void eat(){
System.out.println("人吃饭");
}
public void study(){
System.out.println("人可以学习");
}
}
4.5.1 属性赋值的顺序
* 总结:属性赋值的先后顺序
*
* ① 默认初始化值
* ② 显示初始化
* ③ 构造器初始化
* ④ 通过“对象.方法”或“对象.属性”的方式赋值
*
* 以上操作的先后顺序:① - ② - ③ - ④
4.5.2 JavaBean的概念
package nianwuyin.java1;
/*
* avaBean是一种Java语言写成的可重用组件。
*
* 所谓javaBean,是指符合如下标准的Java类:
类是公共的
有一个无参的公共的构造器
有属性,且有对应的get、set方法
*/
public class Customer {
private int id;
private String name;
public Customer(){
}
public void setId(int i){
id = i;
}
public void setName(String n){
name = n;
}
public int getId(){
return id;
}
public String getString(){
return name;
}
}
4.6 关键字this
package nianwuyin.java2;
/*
* this关键字的使用
* 1.this可以用来修饰、调用:属性、方法、构造器
*
* 2.this修饰属性方法:
* 理解为:当前对象
*
* 2.1在类的方法中,我们可以使用“this.属性”或“this.方法”的方式,来调用当前对象的属性或方法。但是
* 通常情况下,我们都选择省略“this.”。,特殊情况下,如果方法的形参和类的属性同名时,我们必须显式地使用
* “this.变量”的方式来表明此变量是属性而非形参。
* 2.2在类的构造器中,我们可以使用“this.属性”或“this.方法”的方式,来调用当前正在创建对象的属性或方法。但是
* 通常情况下,我们都选择省略“this.”。,特殊情况下,如果构造器的形参和类的属性同名时,我们必须显式地使用
* “this.变量”的方式来表明此变量是属性而非形参。
* 3.this
* ① 在类的构造器中,可以显式使用“this(形参列表)”方式,调用本类指定的其他构造器
* ② 构造器中不能通过“this(形参列表)”方式调用自己
* ③ 如果一个类中有n个构造器,则最多有n - 1个构造器中使用了“this(形参列表)”
* ④ 规定:“this(形参列表)”必须声明在当前构造器的首行
* ⑤ 构造器内部,最多只能声明一个“this(形参列表)”,用来调用其他的构造器
*/
public class ThisTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.setAge(1);
System.out.println(p1.getAge());
p1.eat();
System.out.println();
Person p2 = new Person("Jerrr",20);
System.out.println(p2.getAge());
}
}
class Person{
private String name;
private int age;
public Person(){
// this.eat();
String info = "Person初始化时,需要考虑如下的1,2,3,4...(共40行代码)";
System.out.println(info);
}
public Person(String name){
this();
this.name = name;
}
public Person(int age){
this();
this.age = age;
}
public Person(String name,int age){
this(age);
this.name = name;
// this.age = age;
// //Person初始化时,需要考虑如下的1,2,3,4...(共40行代码)
}
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
public void eat(){
System.out.println("eat");
this.study();
}
public void study(){
System.out.println("study");
}
}
4.7 关键字package、import
package nianwuyin.java2;
import java.lang.reflect.Field;
import java.util.*;
//import java.util.ArrayList;
//import java.util.Arrays;
//import java.util.HashMap;
import nianwuyin.exer4.Account;
import nianwuyin.exer4.Bank;
import nianwuyin.java2.java3.Dog;
import static java.lang.System.*;
import static java.lang.Math.*;
/*
* 一、package关键字的使用
* 1.为了更好地实现项目中的管理,提供包的概念
* 2.使用package声明类或接口所属的包,声明在源文件的首行
* 3.包属于标识符,遵循标识符的命名规则、规范
* 4.每"."一次代表一层文件目录
*
* 补充:同一个包下,不能命名同名的类或接口
* 不同的包下可以命名同名的接口、类
*
* 二、import关键字的使用
*
* 1.在源文件中显式地使用import结构导入指定包下的类、接口
* 2.声明在包的声明和类的声明之间
* 3.如果需要导入多个结构,则并列写出即可
* 4.可以使用“xxx.*”,表示导入xxx包下的所有结构
* 5.如果使用的类或接口时java.lang包下定义的,可以省略import结构
* 6.如果使用的类或接口是本包下定义的,则也可以省略import结构
* 7.如果在源文件中,使用了不同包下同名的类,则必须至少有一个以全类名的方式显式
* 8.如果使用“xxx.*”方式表明可以调用xxx包下的所有结构。但是如果使用xxx子包下的所有结构
* 则仍需要显式导入。
* 9.import static :导入指定类或接口的静态结构:属性或方法
*/
public class PackageImportTest {
public static void main(String[] args) {
String info = Arrays.toString(new int[]{1,2,3});
Bank bank = new Bank();
ArrayList list = new ArrayList();
HashMap map = new HashMap();
Scanner s = null;
System.out.println("hello!");
Person p = new Person();
Account acct = new Account(1000);
//全类名的方式显式
nianwuyin.exer3.Account acct1 = new nianwuyin.exer3.Account(1000, 2000, 0.0123);
Date date = new Date();
java.sql.Date date1 = new java.sql.Date(156161651);
Dog dog = new Dog();
Field field = null;
out.println("hello");
long num = round(123.44);
}
}
五、面向对象(中)
5.1 面向对象的特征二:继承性
package nianwuyin.java;
/*
* 一、面向对象的特征之二:继承性
*
* 一、继承性的好处
* ①减少代码的冗余性,提高了代码的复用性
* ②便于功能的扩展性
* ③为之后多态性的使用,提供了前提
*
* 二、继承性的格式:class A extends B{}
* A:子类、派生类subclass
* B:父类、超类、基类superclass
*
* 2.1体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的结构:属性,方法
* 特别的,父类中声明为private的属性或方法,子类继承父类后,仍然认为获取了父类中私有的结构。
* 只是由于封装性的影响,使得子类不能直接调用父类的结构而已。
* 2.2 子类继承父类以后,可以定义自己特有的属性方法,实现功能的扩展。
* 子类和父类的关系,不同于子集和集合的关系。
*
* 三、java中关于继承性的一些规定
* 1.一个类可以被多个子类继承。
* 2.java类的单继承性:一个类只能有一个父类
* 3.子父类是相对的概念
* 4.子类直接继承的父类称为直接父类。间接继承的父类成为间接父类
* 5.子类继承父类之后,就获取了直接父类以及所有间接父类中声明的属性和方法
*
* 四、
* 1.如果我们没有显式地声明一个类的父类的话,则此类继承于java.lang.Object类
* 2.所有的java类(除java.lang.Object类之外)都直接或间接继承于java.lang.Object类
* 3.意味着多有的java类java.lang.Object类声明的功能
*/
public class ExtendsTest {
public static void main(String[] args) {
Person p1 = new Person();
// p1.age = 1;
p1.eat();
Student s1 = new Student();
s1.eat();
s1.sleep();
s1.name = "Tom";
s1.setAge(10);
System.out.println(s1.getAge());
s1.breath();
Creature c = new Creature();
}
}
5.2 方法的重写
package nianwuyin.overwrite;
/*
* 方法的的重写(override/overwrite)
* 1.重写,子类继承父类之后,可以对父类中同名同参数的方法,进行覆盖操作
*
* 2.重写后,当创建子类对象以后,通过子类调用子父类中的同名参数的方法时,实际执行的是子类重写父类的方法。
*
* 3.重写的规定
* 方法的声明:权限修饰符 返回值类型 方法名(形参列表)throws 异常的类型{方法体}
* 约定俗成:子类:重写的方法,父类:被重写的方法
* ① 子类重写的方法名和形参列表相同与父类被重写方法的方法名和形参列表相同
* ② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
* >特殊情况:子类不能重写父类中protected的方法
* ③ 返回值类型:
* >父类被重写的方法的返回值类型是void,则子类重写的方法的返回值只能是void
* >父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值是A类或A类的子类
* >父类被重写的方法的返回值类型是是基本数据类型,则子类重写的方法的返回值是相同的基本数据类型。
* ④ 子类重写的方法异常类型不大于父类重写的方法抛出的异常类型(具体放到异常处理时候讲)
* ********************************************************************************
* 子类和父类同名同参数的方法都声明为非static的(考虑重写),要么都声明为非static的(不是重写)。
* 面试题:区分方法的重载与重写
*/
public class OverWriteTset {
public static void main(String[] args) {
Student s = new Student("CS");
s.eat();
s.walk();
s.study();
Person p1 = new Person();
p1.eat();
}
}
5.3 关键字:super
package nianwuyin.supertest;
/*
* super关键字的使用
* 1.super键理解为:父类的
* 2.可以用来调用属性、方法、构造器
*
* 3.super的使用属性和方法
* 3.1我们可以在子类的方法或构造器中,使用“super.属性/方法”的方式,显式调用
* 父类中的声明的属性或方法,但是,通常情况下,我们习惯省略“super”
* 3.2特殊情况下,当子类和父类定义了同名的属性时,我们要在子类调用父类中声明的属性时,
* 则必须显式使用super.属性的方式,表明调用父类中的属性
* 3.3特殊情况下,当子类和父类定义了同名的方法时,我们要在子类调用父类中声明的方法时,
* 则必须显式使用super.方法的方式,表明调用父类中的方法
*
* 4.super调用构造器
* 4.1我们可以在子类的构造器显式地使用“super(形参列表)”的方式调用父类中声明的指定的构造器
* 4.2“super(形参列表)”必须声明在子类首行!
* 4.3我们在类的构造器中,针对于“this(形参列表)”或“super(形参列表)”只能二选一,不能同时出现
* 4.4在构造器的首行,没有显式声明“this(形参列表)”或“super(形参列表)”,默认调用父类中空参的构造器“super()”
* 4.5在类的多个构造器中,至少有一个类的构造器使用了“super(形参列表)”,调用父亲中的构造器
*/
public class SuperTest {
public static void main(String[] args) {
Student s = new Student();
s.show();
s.study();
Student s1 = new Student("TOM",21,"IT");
s1.show();
}
}
5.4 子类对象实例化的全过程
package nianwuyin.instance;
/*
* 子类对象实例化的全过程
*
* 1.从结果上来看(继承性)
* 子类继承父类以后,就获取了父类中声明的属性或方法
* 创建子类的对象,在堆空间中,就会加载所有父类中的属性
*
* 2.从过程上来看
* 当我们通过子类的构造器创建子类对象时,我们一定会直接或间接地调用其父类的构造器,进而间接调用父类的父类的构造器,
* 知道调用了Object类中的构造器中。正因为加载过所有父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以
* 考虑调用。
*
* ,明确:虽然创建子类对象时,调用了父类构造器,但是自始至终只new了一个对象。
*/
public class InstanceTest {
}
5.5 面向对象的的特征三:多态性
package nianwuyin.duotaixing;
* 面向对象特征之三:多态性
*
* 1.理解多态性:可以理解为一个事物的多种形态。
* 2.何为多态性
* 对象的多态性:父类的引用指向子类的对象
*
* 3.多态的使用,虚拟方法调用
* 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法
* 总结,编译看左边,运行看右边
*
* 4.多态性的使用前提:
* ①要有类的继承关系
* ②方法的重写
*
* 5.对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
*
* ****************************************************
*
*
public class PersonTest {
public static void main(String[] args) {
Person p1 = new Person();
p1.eat();
Man man = new Man();
man.eat();
man.age = 25;
man.earnMoney();
//***************************************************
System.out.println("***************************************************");
//对象的多态性:父类的引用指向子类的对象
Person p2 = new Man();
//多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法---虚拟方法调用
p2.eat();
p2.walk();
// Person p3 = new Woman();
System.out.println("**********************************************************");
//不能调用子类所特有的方法、属性,编译时是p2是Person类型
// p2.earnMoney();
p2.name = "Tom";
// p2.isSmoking = true;
//有了对象的多态性后,内存中实际是加载了子类特有的属性和方法,但是由于变量声明为父类类型,导致编译时
//,只能调用父类声明的属性和方法,子类特有的属性和方法不能调用
//如何才能调用子类的属性和方法?
//向下转型:使用强制类型转换符
Man m1 = (Man)p2;
m1.earnMoney();
m1.isSmoking = true;
//使用强转时,可能出现ClassCastException
// Woman w1 = (Woman)p2;
// w1.goShopping();
* instanceof关键字的使用
*
* a instanceof A:判断对象a是否是类A的实例。如果是,返回true。如果不是,返回false
*
* 使用情境:为了避免在向下转型时出现异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,进行向下转型
* 如果返回false,就不进行向下转型。
*
* 如果a instanceof A返回true,则a instanceof B也返回true
* 其中,类B是类A的父类。
if(p2 instanceof Woman){
Woman w1 = (Woman)p2;
w1.goShopping();
System.out.println("********Woman********");
}
if(p2 instanceof Man){
Man m2 = (Man)p2;
m2.earnMoney();
System.out.println("********Man********");
}
//练习
//问题一:编译通过,运行不通过
//举例一
// Person p3 = new Woman();
// Man m3 = (Man)p3;
// 举例二
// Person p4 = new Person();
// Man m4 = (Man)p4;
//问题二:编译,运行也通过
// Object obj = new Woman();
// Person p = (Person)obj;
//问题二:编译不过
// Man m5 = new Woman;
// String str = new Date();
// Object o = new Date();
// String str1 = (String)o;
}
}
【多态性面试题】
1.谈谈你对多态性的理解?
① 实现代码的通用性
②Object类中定义的public boolean equals(Object obj){}
JDBC:使用java程序操作(获取数据库,CRUD)数据库(MySQL、Oracle、DB2、SQL、Server)
③抽象类、接口的使用肯定体现了多态性。(抽象类、接口不能实例化)
2.多态是编译时行为还是运行时行为?
package com.atguigu.test;
import java.util.Random;
//面试题:多态是编译时行为还是运行时行为?
//证明如下:
class Animal {
protected void eat() {
System.out.println("animal eat food");
}
}
class Cat extends Animal {
protected void eat() {
System.out.println("cat eat fish");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("Dog eat bone");
}
}
class Sheep extends Animal {
public void eat() {
System.out.println("Sheep eat grass");
}
}
public class InterviewTest {
public static Animal getInstance(int key) {
switch (key) {
case 0:
return new Cat ();
case 1:
return new Dog ();
default:
return new Sheep ();
}
}
public static void main(String[] args) {
int key = new Random().nextInt(3);
System.out.println(key);
Animal animal = getInstance(key);
animal.eat();
}
}
5.6 Object类的使用
5.6.1 java.lang.Object类的说明
1.Object类是所有Java类的根父类
2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
3.Object类中的功能(属性、方法)就具有通用性。属性:无;方法:equals()/to String()/getClass()/hashCode/clone()/finlaize()/wait()/notify()/notifyAll()
4.Object类只声明了一个空参构造器
5.6.2 equals()方法
package nianwuyin.java;
import java.util.Date;
/*
* 面试题和equals的区别:
* 一、回顾==的使用
* ==:运算符
* 1. 可以使用在基本数据类型变量和引用数据变量
* 2. 如果比较的是基本数据变量,比较两个变量保存的数据是否相等。(不一定类型相同)
* 如果比较的是引用数据类型,比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体
* 补充:==符号使用时,必须保证符号左右两边的变量类型一致
*
*
* 二、equals()方法的使用
* 1. 是一个方法,而非运算符
* 2. 只能适用于引用数据类型
* 3. Object类中equals()的定义
* public boolean equals(Object obj) {
return (this == obj);
}
* 说明:Object类中的equals()方法和==的作用是相同的。
* 4.像String、Date、File、包装类等都重写了Object类中的equals()方法,重写以后不是
* 两个引用的地址是否相同,而是比较两个对象的“实体内容”是否相同。
*
* 5.通常情况下,我们自定义的类如果适用equals()的话,也通常是比较两个对象的实体内容是否相同,那么需要
* 对Object类中equals()方法进行重写
* 重写的原则,比较两个对象的实体内容是否相同
*/
public class EqualsTest {
public static void main(String[] args) {
// ==基本数据类型
int i = 10;
int j = 10;
double d = 10.0;
System.out.println(i == j);// true
System.out.println(i == d);// true
boolean b = true;
// System.out.println(i==b);
char c = 10;
System.out.println(i == c);// ture
// ==引用数据类型
Customer cust1 = new Customer("Tom", 21);
Customer cust2 = new Customer("Tom", 21);
System.out.println(cust1 == cust2);// false
String s1 = new String("nianwuyin");
String s2 = new String("nianwuyin");
// equals()
System.out.println("***********************");
System.out.println(cust1.equals(cust2));// false
System.out.println(s1.equals(s2));// true
Date date1 = new Date(3245252532L);
Date date2 = new Date(3245252532L);
System.out.println(date1.equals(date2));// true
}
}
class Customer {
String name;
int age;
public Customer(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//自动生成equals()
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Customer other = (Customer) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Customer [name=" + name + ", age=" + age + "]";
}
//重写的原则,比较两个对象的实体内容是否相同
//手动实现equals()的重写
// public boolean equals(Object obj) {
// if (this == obj) {
// return true;
// }
//
// if(obj instanceof Customer){
// Customer cust = (Customer)obj;
// //比较两个对象的每个属性是否都相同
if(this.age == cust.age && this.name.equals(cust.name)){
return true;
}else{
return false;
}
// return this.age == cust.age && this.name.equals(cust.name);
// }
//
// return false;
// }
//手动实现
// @Override
// public String toString() {
// return "Customer[name = " + name + ",age = " + age + "]";
// }
}
5.6.3 toString()方法
package nianwuyin.java;
import java.util.Date;
/*
* Object类中toString()的使用
* 1. 当我们输出输出一个对象的引用时,实际上就是调用当前对象的toString()
*
* 2. Object类中同String()的定义
* public String toString() {
* return getClass().getName() + "@" + Integer.toHexString(hashCode());
* }
*
* 3. 像String、Date、File、包装类都重写了Object类中的toString()方法
* 使得在调用对象的toString()时,返回“实体内容”信息
*
* 4. 自定义类也可以重写toString方法,当调用此方法时,返回实体内容信息
*
*/
public class toString {
public static void main(String[] args) {
Customer cust1 = new Customer("Tom", 21);
System.out.println(cust1.toString());//nianwuyin.java.Customer@15db9742
System.out.println(cust1);//nianwuyin.java.Customer@15db9742
String str = new String("MM");
System.out.println(str);//MM
Date date = new Date(4534534534543L);
System.out.println(date.toString());//
}
}
5.7 单元测试方法
package nianwuyin.java1;
import java.util.Date;
import org.junit.Test;
/*
* Java中的JUnitTest单元测试
*
* 步骤:
* 1. 选中当前工程-右键选择:build path - add libraries - JUnit 4 - 下一步
* 2. 创建java类进行单元测试
* 此时java类要求:① 此类是公共的 ②此类提供公共的无参的构造器
* 3. 此类中声明单元测试方法,方法的权限是public,没有返回值,没有形参
*
* 4. 此单元测试方法上需要声明声明@Test,并在单元测试类中导入:import.org.junit.Test
*
* 5. 声明好单元测试方法以后,就可以在方法体内测试相关的代码
* 6.写完代码以后,左键 双击方法名 右键 run as JUnit
*
* 说明:
* 1.如果执行结果没有任何异常。绿条
* 2.如果执行结果出现异常。红条
*/
public class JUnitTest {
int num = 10;
@Test
public void testEquals(){
String s1 = "MM";
String s2 = "MM";
System.out.println(s1.equals(s2));
// Object obj = new String("GG");
// Date date = (Date)obj;
System.out.println(num);
}
}
5.8 包装类的使用
package nianwuyin.java1;
import org.junit.Test;
/*
* 包装类的使用:
* 1.java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
*
* 2. 掌握,基本数据类型、包装类、String三者之间的转换
*/
public class WrapperTest {
// String ---> 基本数据类型、包装类:调用包装类的parseXxx()
@Test
public void Test5() {
String str1 = "123";
//错误的情况
// int num1 = (int)str1;
// Integer in1 = (Integer)str1;
int num2 = Integer.parseInt(str1);
System.out.println(num2 + 1);
}
// 基本数据类型、包装类 ---> 为String类型,调用String重载的valueOf(Xxx xxx)
@Test
public void Test4() {
int num1 = 10;
// 方式一
String str1 = num1 + ";";
// 方式二:String重载的valueOf(Xxx xxx)
float f1 = 12.3f;
String str2 = String.valueOf(f1);
Double d1 = new Double(12.4);
String str3 = String.valueOf(d1);
System.out.println(str2);
System.out.println(str3);
}
// JDK 5.0新特性:自动装箱与自动拆箱
@Test
public void test3() {
// int num1 = 10;
// //基本数据类型--->包装类的对象
// method(num1);
// 自动装箱
int num2 = 10;
Integer in1 = num2;
boolean b1 = true;
Boolean b2 = b1;
// 自动拆箱
System.out.println(in1.intValue());
int num3 = in1;
}
public void method(Object obj) {
System.out.println(obj);
}
// 包装类 ---->基本数据类型:调用包装类xxxValue
@Test
public void Test2() {
Integer in1 = new Integer(12);
int i1 = in1.intValue();
System.out.println(i1 + 1);
Float f1 = new Float(12.3);
float f2 = f1.floatValue();
System.out.println(f2 + 1);
}
// 基本数据类型---->包装类:调用包装类的构造器
@Test
public void test1() {
int num1 = 10;
Integer in1 = new Integer(num1);
System.out.println(in1.toString());
Integer in2 = new Integer("123");
System.out.println(in2.toString());
// 报异常
// Integer in3 = new Integer("123abc");
// System.out.println(in3.toString());
Float f1 = new Float(12.3f);
Float f2 = new Float("12.3");
Boolean b1 = new Boolean(true);
Boolean b2 = new Boolean("true");
Boolean b3 = new Boolean("true123");
System.out.println(b3);// false
Order o = new Order();
System.out.println(o.isMale);// false
System.out.println(o.isFemale);// null
}
}
class Order {
boolean isMale;
Boolean isFemale;
}
六、面向对象(下)
6.1 关键字:static
static:静态的
6.1.1 可以用来修饰的结构:主要用来修饰类的内部结构
属性、方法、代码块、内部类
6.1.2 static修饰属性:静态变量或类变量
* 2.使用static修饰属性:静态变量、类变量
* 2.1 属性,按是否使用static修饰,又分为:静态属性 vs 非静态变量(实例变量)
* 实例变量:我们创建了类的多个对象,每个对象都独立拥有一套类中的非静态属性。当修改其中一个对象中的
* 非静态属性,不会导致其他对象中同样的属性值的修改。
* 静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致
* 其他对象调用此静态变量时,是修改过的了。
* 2.2 static修饰属性的其他说明:
* ① 静态变量随着类的加载而加载。可以通过“类.静态变量”的方式进行调用
* ② 静态变量的加载要早于对象的创建。
* ③ 由于类只会加载一次,则静态变量在内存中也会存在一份:存在方法区的静态域中。
*
* ④ 类变量 实例变量
* 类 yes no
* 对象 yes yes
*
* 2.3 静态属性举例:System.out; Math.PI
6.1.3 静态变量的内存解析
6.1.4 static修饰方法:静态方法、类方法
① 随着类的加载而加载,可以通过“类.静态方法”的方式进行调用
② 静态方法 非静态方法
类 yes no
对象 yes yes
③ 静态方法中只能调用只能调用静态方法或属性
非静态方法中,既可以调用非静态的方法和属性,也可以调用静态的方法和属性
6.1.5 static的注意点
> 在静态的方法内,不能使用this、super关键字
> 关于静态属性和静态方法的使用,从生命周期的角度去理解
6.1.6 如何判定属性和方法应该使用static关键字:
属性:
> 属性是可以被多个对象所共享的,不会随着对象不同而不同的。
> 类中的常量也常常声明为static
方法:
> 操作静态属性的方法,通常设置为static的
> 工具类的方法,习惯上声明为static的。比如:Math、Arrays、Collections
6.1.7 使用举例
举例一: Math、Arrays、Collections等工具类
举例二:单例模式
举例三:
package nianwuyin.java1;
/*
* static关键字的应用
*/
public class CircleTest {
public static void main(String[] args) {
Circle c1 = new Circle();
Circle c2 = new Circle();
Circle c3 = new Circle(3.4);
System.out.println("c1 id:" + c1.getId());
System.out.println("c2 id:" + c2.getId());
System.out.println("c3 id:" + c3.getId());
System.out.println("Circle: " + Circle.getTotal());
}
}
class Circle {
private double radius;
private int id;//自动赋值
public Circle(){
id = init++;
total++;
}
public Circle(double radius){
this();
// id = init++;
// total++;
this.radius = radius;
}
private static int total;//记录创建圆的个数
private static int init = 1001;//static声明的属性被所有对象所共享
public double findArea() {
return 3.14 * radius * radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public int getId() {
return id;
}
public static int getTotal() {
return total;
}
}
6.1.8 设计模式:单例模式
1.设计模式的说明:设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。常用:5种创建型、7种结构型、11种行为型
2.单例模式
要解决的问题:所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例
具体代码的实现:饿汉式 vs 懒汉式
> 饿汉式:坏处:对象加载时间过长;好处:线程安全的
> 懒汉式:好处:延迟对象的创建;目前写法的坏处:线程不安全 ---> 到多线程内容时,再修改
//饿汉式1
class Bank{
//1.私有化类的构造器
private Bank(){
}
//2.内部创建类的对象
//4.要求此对象也必须声明为静态的
private static Bank instance = new Bank();
//3.提供公共静态的方法,返回类的对象
public static Bank getInstance(){
return instance;
}
}
//饿汉式2
class Order {
// 1.私有化类的构造器
public Order() {
}
// 2.声明当前类对象,没有初始化
// 4.此对象也必须声明为static
private static Order instance = null;
static{
instance = new Order();
}
// 3.声明public、static的返回当前类的对象的方法
public static Order getInstance() {
if (instance == null) {
instance = new Order();
}
return instance;
}
//懒汉式
class Order {
// 1.私有化类的构造器
public Order() {
}
// 2.声明当前类对象,没有初始化
// 4.此对象也必须声明为static
private static Order instance = null;
// 3.声明public、static的返回当前类的对象的方法
public static Order getInstance() {
if (instance == null) {
instance = new Order();
}
return instance;
}
}
6.2 main()方法的使用
6.2.1 main()方法作为程序的入口
6.2.2 main()也是一个普通的静态方法
6.2.3 main()方法也可以作为我们与控制台交互的方式。(之前,使用Scanner)
如何将控制台获取的数据传给形参:String[ ] args?
运行时:java 类名 "Tom" "Jessry" "123" "ture"
sysout(args[0]);//"Tom"
sysout(args[3]);//"true" ---> Boolean.parseBoolean(args[3]);
sysout(args[4]);//报异常
6.2.4 小结:public static void main(String[] args) {//方法体}
public static void main(String[] args) {//方法体}
权限修饰符:private、缺省、protected、public ---> 封装性
修饰符: static、final、abstract、native 可以用来修饰方法
返回值类型:无返回值 / 有返回值 ---> return
方法名:需要满足标识符命名的规则、规范。见名知意
形参列表:重载 vs 重写;参数的值传递机制;体现对象的多态性
方法体:主要来体现功能的排序
6.3 类的成员之四:代码块
6.3.1 代码块的作用:用来初始化类、对象
6.3.2 代码块如果有修饰的话,只能使用static
6.3.3 分类:静态代码块 vs 非静态代码块
静态代码块:
> 内部可以有输出语句
> 随着类的加载而执行,而且只执行一次
> 作用:初始化类的信息
> 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
> 静态代码块的执行优先于非静态代码块的执行
> 静态代码块内只能调用静态的属性、方法,不能调用非静态的结构
非静态代码块:
> 内部可以有输出语句
> 随着对象的创建而执行
> 每创建一个对象,就执行一次非静态代码块
> 作用:可以在创建对象时,对对象的属性等进行初始化
> 如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
> 非静态代码块内可以调用静态的属性、方法,也可以调用非静态的结构
6.3.4 实例化子类对象时,涉及到父类、子类中静态代码块、非静态代码块、构造器的加载顺序
【由父及子,静态先行】
package nianwuyin.java3;
//总结:由父及子,静态先行
class Root{
static{
System.out.println("Root的静态初始化块");
}
{
System.out.println("Root的普通初始化块");
}
public Root(){
System.out.println("Root的无参数的构造器");
}
}
class Mid extends Root{
static{
System.out.println("Mid的静态初始化块");
}
{
System.out.println("Mid的普通初始化块");
}
public Mid(){
System.out.println("Mid的无参数的构造器");
}
public Mid(String msg){
//通过this调用同一类中重载的构造器
this();
System.out.println("Mid的带参数构造器,其参数值:"
+ msg);
}
}
class Leaf extends Mid{
static{
System.out.println("Leaf的静态初始化块");
}
{
System.out.println("Leaf的普通初始化块");
}
public Leaf(){
//通过super调用父类中有一个字符串参数的构造器
super("尚硅谷");
System.out.println("Leaf的构造器");
}
}
public class LeafTest{
public static void main(String[] args){
new Leaf();
//new Leaf();
}
}
package nianwuyin.java3;
class Father {
static {
System.out.println("11111111111");
}
{
System.out.println("22222222222");
}
public Father() {
System.out.println("33333333333");
}
}
public class Son extends Father {
static {
System.out.println("44444444444");
}
{
System.out.println("55555555555");
}
public Son() {
System.out.println("66666666666");
}
public static void main(String[] args) { // 由父及子 静态先行
System.out.println("77777777777");
System.out.println("************************");
new Son();
System.out.println("************************");
new Son();
System.out.println("************************");
new Father();
}
}
6.3.5 属性的赋值顺序
① 默认初始化
② 显式初始化/⑤ 代码块中赋值
③ 构造器初始化
④ 有了对象以后,可以通过“对象.属性”或“对象.方法”,进行赋值
6.4 关键字:final
修饰的结构:类、方法、变量
① final 用来修饰一个类:此类不能被其他类所继承;比如:String类、System类、StringBuffer类
② final 用来修饰一个方法:此方法不能被重写;比如:Object类中的getClass();
③ final 用来修饰变量:此时的“变量”就称为是一个常量
> final修饰属性:可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化
> final修饰局部变量:尤其是final修饰型形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能重新赋值。
④ static final 用来修饰属性:全局变量
6.5 关键字:abstract
6.5.1 修饰的结构:类、方法
6.5.2 修饰类:抽象类
> 此类不能实例化
> 抽象类中一定有构造器,便于子类对象实例化的时候调用(涉及:子类对象实例化的全过程)
> 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作
6.5.3 修饰方法:抽象方法
> 抽象方法只有方法的声明,没有方法体
> 包含抽象方法的类一定是一个抽象类。反之,抽象类中是可以没有抽象方法的。
> 若子类重写了父类中的所有抽象方法后,此子类方可实例化。若子类没有重写父类中的所有抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
6.5.4 注意点
> abstract不能用来修饰:属性、构造器等结构
> abstract不能用来修饰私有方法、静态方法、final的方法、final的类
6.5.5 应用举例:
举例一:
举例二:
public abstract class GeometricObject {//几何图形
public abstract double findArea();
}
public class Circle extends GeometricObject{
private double radius;
public double findArea(){
return Math.PI * radius * radius;
}
}
举例三:IO流中涉及到的抽象类:InputStream / OutputStream / Reader / Writer。在其内部定义了抽象的read()、write()方法。
6.5.6 模板方法的设计模式
> 在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
>应用场景:
数据库访问的封装
Junit单元测试
JavaWeb的Servlet中关于doGet/doPost方法调用
Hibernate中模板程序
Spring中JDBCTemlate、HibernateTemplate等
package nianwuyin.java;
/*
* 抽象类的应用:模板方法的设计模式
*
*
*/
public class TemplateTest {
public static void main(String[] args) {
Template t = new SubTemplate();
t.spendTime();
}
}
abstract class Template {
// 用来计算某段代码执行所需要花费的时间
public void spendTime() {
long start = System.currentTimeMillis();
code();// 不确定的部分,易变的部分
long end = System.currentTimeMillis();
System.out.println("Time: " + (end - start));
}
public abstract void code();
}
class SubTemplate extends Template {
@Override
public void code() {
for(int i = 2;i<=1000;i++){
boolean isFlag = true;
for(int j = 2;j <= Math.sqrt(i);j++){
if(i % j == 0){
isFlag = false;
break;
}
}
if(isFlag){
System.out.println(i);
}
}
}
}
6.6 接口
6.6.1 使用说明
* 接口的使用
* 1.接口使用interface来定义
* 2.java中,接口和类是并列的两个结构
* 3.如何定义接口,定义接口中的成员
*
* 3.1 JDK7以及以前:只能定义全局常量和抽象方法
* > 全局常量:public static final,但是书写时,可以省略不写
* > 抽象方法:public abstract
*
* 3.2 JDK8:除了定义全局常量和抽象方法之外;还可以定义静态方法、默认方法(略)
*
* 4.接口中不能定义构造器的!接口不可以实例化
*
* 5.java开发中,接口都通过让类去实现(implements)的方式来使用,
* 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
* 如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
*
* 6.java类可以实现多个接口 ---> 弥补java类单继承性的局限性
* 格式: class AA extends BB implements CC,DD,EE
*
* 7.接口与接口之间可以继承,而且可以多继承
* ***********************************************
* 8.接口的具体使用,能够体现多态性
* 9.接口实际上可以看作是一种规范
6.6.2 面向接口编程的思想
我们在应用程序中,调用的结构都是JDBC中定义的接口,不会出现具体某一个数据库厂商的API
package nianwuyin.java1;
/*
* 接口的使用
* 1.接口使用上满足多态性
* 2.接口实际上就定义了一个规范
* 3.开发中,体会面向接口编程!
*
*/
public class USBTest {
public static void main(String[] args) {
Computer com = new Computer();
// 1.创建了接口的非匿名实现类的非匿名对象
Flash flash = new Flash();
com.transferData(flash);
// 2.创建了接口的非匿名实现类的匿名对象
com.transferData(new Printer());
// 3.创建接口的匿名实现类的非匿名对象
USB phone = new USB() {
@Override
public void start() {
System.out.println("phone start");
}
@Override
public void stop() {
System.out.println("phone stop");
}
};
com.transferData(phone);
// 4.创建接口的匿名实现类的匿名对象
com.transferData(new USB(){
@Override
public void start() {
System.out.println("mp3 start");
}
@Override
public void stop() {
System.out.println("mp3 stop");
}
});
}
}
class Computer {
public void transferData(USB usb) {// USB usb = new Flash();
usb.start();
System.out.println("USB details");
usb.stop();
}
}
interface USB {
// 定义长、宽、最大最小传输速度
void start();
void stop();
}
class Flash implements USB {
@Override
public void start() {
System.out.println("USB start");
}
@Override
public void stop() {
System.out.println("USB stop");
}
}
class Printer implements USB {
@Override
public void start() {
System.out.println("Printer start");
}
@Override
public void stop() {
System.out.println("Printer stop");
}
}
6.6.3 Java 8中关于接口的新规范
知识点1:接口中定义的静态方法,只能通过接口来调用
知识点2:通过类的对象,可以调用接口中的默认方法。如果实现类重写了接口中的默认方法,则调用时,仍然调用的是重写以后的方法
知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名的同参数的方法,那么子类在没有重写此方法的情况下,默认调用的是父类中同名同参数的方法。---> 类优先原则
知识点4:如果实现类实现了多个接口,而多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错。 ---> 接口冲突。这就需要我们必须在实现类中重写此方法
知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
6.6.4 面试题
抽象类和接口的异同?
相同点:不能实例化;都可以包含抽象方法
不同点:
> 把抽象类和接口(java7,java8,java9)的定义、内部结构解释说明
> 类:单继承性 接口:多继承性 类与接口多实现
6.6.5 代理模式
代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。
【应用场景】
【应用举例】
package nianwuyin.java1;
/*
* 接口的应用:代理模式
*/
public class NetWorkTest {
public static void main(String[] args) {
Server server = new Server();
//server.browse();
ProxyServer proxyServer = new ProxyServer(server);
proxyServer.browse();
}
}
interface NetWork{
public void browse();
}
//被代理类
class Server implements NetWork{
@Override
public void browse() {
System.out.println("real server");
}
}
//代理类
class ProxyServer implements NetWork{
private NetWork work;
public ProxyServer(NetWork work){
this.work = work;
}
public void check(){
System.out.println("check");
}
@Override
public void browse() {
check();
work.browse();
}
}
6.6.6 工厂的设计模式
解决的问题:实现了创建者与调用者的分离 即将创建对象的具体过程 屏蔽 隔离起来,达到 提高 灵活性的 目的 。
具体的模式:
> 简单工厂模式: 用来生产同一等级结构中的任意产品。(对于增加新的产品,需要修改已有代码)
> 工厂方法模式: 用来生产同一等级结构中的固定产品。(支持增加任 意产品)
> 抽象工厂模式: 用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)
6.7 类的结构之五:内部类
6.7.1 使用说明
* 类的内部成员之五:内部类
* 1. Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
*
* 2.内部类的分类:成员内部类(静态、非静态) vs 局部内部类(方法内、代码块内、构造器内)
*
* 3.成员内部类:
* 一方面:作为外部类的成员:
* > 调用外部类的结构
* > 可以被static修饰
* > 可以被4种不同的权限修饰
*
* 另一方面,作为一个类:
* > 类内可以定义属性、方法、构造器
* > 可以被final修饰,表示此类不能被继承。不使用,可以被继承
* > 可以被abstract修饰,
*
* 4.关注如下3个问题
* 4.1 如何实例化成员内部类的对象
* 4.2 如何在成员内部类种区分调用外部类的结构
* 4.3 开发中,局部内部类的使用 见<InnerClassTest1.java>
6.7.2 如何实例化成员内部类的对象及在成员内部类种区分调用外部类的结构
public class InnerClassTest {
public static void main(String[] args) {
// 创建Dog实例(静态的成员内部类)
Person.Dog dog = new Person.Dog();
dog.show();
// 创建Bird实例(非静态的成员内部类)
// Person.Bird bird = new Person.Bird();//错误的
Person p = new Person();
Person.Bird bird = p.new Bird();
bird.sing();
System.out.println();
bird.display("huangli");
}
}
class Person {
String name = "ren";
int age;
public void eat() {
System.out.println("eat");
}
// 静态成员内部类
static class Dog {
String name;
int age;
public void show() {
System.out.println("show");
}
}
// 非静态成员内部类
protected class Bird {
String name = "niao";
public Bird() {
}
public void sing() {
System.out.println("sing");
Person.this.eat();// 调用外部类的非静态属性
eat();
System.out.println(age);
}
public void display(String name) {
System.out.println(name);// 方法的形参
System.out.println(this.name);// 内部类的属性
System.out.println(Person.this.name);// 外部类的属性
}
}
public void method() {
// 局部内部类
class AA {
}
}
{
// 局部内部类
class BB {
}
}
public Person() {
// 局部内部类
class CC {
}
}
}
6.7.3 开发中,局部内部类的使用
public class InnerClassTest1 {
//开发中,很少见
public void method(){
//局部内部类
class AA{
}
}
//返回一个实现了Comparable接口的类的对象
public Comparable getComparable(){
//创建一个实现了Comparable接口的类:局部内部类
//方式一:
// class MyComparable implements Comparable{
//
// @Override
// public int compareTo(Object o) {
// // TODO Auto-generated method stub
// return 0;
// }
//
// }
//
// return new MyComparable();
//方式二:
return new Comparable(){
@Override
public int compareTo(Object o) {
return 0;
}
};
}
}
6.7.4 注意点
① 在局部内部类的方法(比如show)中,如果调用局部内部类所声明的方法中(比如method)的局部变量的话, 要求此局部变量声明为final的。
jdk 7及之前的版本,要求此局部变量显式地声明为final的; jdk 8及之后的版本,可以省略final的声明
public void method() {
// 局部变量
int num = 10;
class AA {
public void show() {
// num = 20;
System.out.println(num);
}
}
}
② 成员内部类和局部内部类,在编译以后,都会生成字节码文件。
格式:成员内部类:外部类$内部类名.class
局部内部类:外部类$数字 内部类.calss
七、异常处理
7.1 异常结构、编译时异常、运行时异常
编译时异常:执行javac.exe命令时,可能出现的异常
运行时异常:执行java.exe命令时,出现的异常
7.2 异常的处理
* 过程一:“抛”:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。
* 并将此对象抛出。
* 一旦抛出对象以后,其后的代码就不再执行。
*
* 关于异常对象的产生:
* ① 系统自动生成的异常对象
* ② 手动地生成一个异常对象,并抛出(throw)
*
* 过程二:“抓”:可以理解为异常的处理方式:① try-catch-finally ②throws
7.2.1 try-catch-finally的使用
* 二、try-catch-finally的使用
* try{
* //可能出现异常的代码
* }catch(异常类型1 变量名1){
* //处理异常的方式1
* }catch(异常类型2 变量名2){
* //处理异常的方式2
* }
* ...
* finally{
* //一定会执行的代码
* }
*
* 说明:
* 1. finally 是可选的
* 2. 使用try将可能实现异常的代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的
* 类型,去catch进行匹配。
* 3. 一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理,一旦处理完成,就跳出当前的try-catch结构
* (在没有写finally的情况下)。继续执行其后的代码
* 4. catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓
* catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面,否则报错
* 5. 常用的异常对象处理的方式:① String getMessage() ②printStackTrace()
* 6. 在try结构中声明的变量,在出了try结构以后,就不能再被调用
* 7. try-catch-finally结构可以嵌套
7.2.2 如何看待编译时异常和运行时异常?
体会1:使用try-catch-finally处理编译时异常,使得程序在编译时就不再报错,但是在 运行时仍可能报错相当于我们使用try-catch-finally将一个编译时可能出现的异常,延时到运行时出现;
体会2:开发中,由于运行时异常比较常见,所以我们通常不针对运行时异常编写try-catch-finally了。针对编译时异常,我们说一定要考虑异常的处理。
7.2.3 finally
* 1. finally是可选的
* 2. finally中声明的是一定会被执行的代码,即使catch中又出现异常了,try中有return语句,catch中有
* return语句等情况
* 3. 像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动回收的,我们需要自己手动地进行资源的释放。
* 此时的资源释放,就需要声明在finally中。
7.2.4 final、finally、finalize三者之间的区别?
名称类似:
throw和throws
Collection和Collections
String、StringBuffer、StringBuilder
ArrayList、LinkedList
HashMap、LinkedHashMap
重写、重载
名称不类似:
抽象类、接口
==、equals
sleep()、wait()
7.2.5 throws的使用
* 1.“throws + 异常类型”写在方法的声明处。指出此方法时,可能会抛出的异常类型。
* 一旦方法体执行时,出现异常,仍会在异常的代码处生成一个异常类的对象,此对象满足throws后异常
* 类型时,就会被抛出。异常代码后面的代码就不再执行!
7.2.6 两种处理方式的对比及选择
* 1.体会:try-catch-finally:真正地将异常处理掉了。
* throws的方式只是将异常抛给方法的调用者。并没有真正地将异常处理掉。
*
* 2. 开发中如何选择使用try-catch-finally,还是使用throws?
* 2.1 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果
* 子类重写的方法中有异常,必须使用try-catch-finally方式处理。
* 2.2 执行的方法中,先后又调用了另外几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws
* 的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。
7.2.7 【补充】方法重写的规则之一
子类重写的方法排除的异常类型不大于父类被重写的方法抛出的异常类型
7.3 手动抛出异常对象
在程序执行时,除了自动抛出异常对象的情况之外,我们还可以手动地throw一个异常类的对象。
throw:表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内。
throws:属于异常处理的一种非异常处理的一种方式,声明在方法的声明处。
【典型例题】
package nianwuyin.java2;
public class StudentTest {
public static void main(String[] args) {
try {
Student s = new Student();
s.regist(-1001);
System.out.println(s);
} catch (Exception e) {
// e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
class Student {
private int id;
public void regist(int id) throws Exception {
if (id > 0) {
this.id = id;
} else {
// System.out.println("illegal!");
// 手动抛出一个异常
// throw new RuntimeException("illegal!");
// throw new Exception("illegal!");
throw new MyException("Not negative number");
}
}
@Override
public String toString() {
return "Student [id=" + id + "]";
}
}
7.4 自定义异常类
/*
* 如何自定义异常类
* 1. 继承现有的异常结构:RuntimeException、Exception
* 2. 提供全局常量:serialVersionUID
* 3. 提供重载的构造器
*
*/
public class MyException extends Exception {
static final long serialVersionUID = -7034897132235766939L;
public MyException() {
}
public MyException(String msg) {
super(msg);
}
}