Java基础是学习JavaEE、大数据、Android开发的基石!
学东西的:3w(what、why、how)是什么?、为什么?、怎么用?
.
.
.
Eclipse中的快捷键:
* 1.补全代码的声明:alt + /
* 2.快速修复: ctrl + 1
* 3.批量导包:ctrl + shift + o
* 4.使用单行注释:ctrl + /
* 5.使用多行注释: ctrl + shift + /
* 6.取消多行注释:ctrl + shift + \
* 7.复制指定行的代码:ctrl + alt + down 或 ctrl + alt + up
* 8.删除指定行的代码:ctrl + d
* 9.上下移动代码:alt + up 或 alt + down
* 10.切换到下一行代码空位:shift + enter
* 11.切换到上一行代码空位:ctrl + shift + enter
* 12.如何查看源码:ctrl + 选中指定的结构 或 ctrl + shift + t
* 13.退回到前一个编辑的页面:alt + left
* 14.进入到下一个编辑的页面(针对于上面那条来说的):alt + right
* 15.光标选中指定的类,查看继承树结构:ctrl + t
* 16.复制代码: ctrl + c
* 17.撤销: ctrl + z
* 18.反撤销: ctrl + y
* 19.剪切:ctrl + x
* 20.粘贴:ctrl + v
* 21.保存: ctrl + s
* 22.全选:ctrl + a
* 23.格式化代码: ctrl + shift + f
* 24.选中数行,整体往后移动:tab
* 25.选中数行,整体往前移动:shift + tab
* 26.在当前类中,显示类结构,并支持搜索指定的方法、属性等:ctrl + o
* 27.批量修改指定的变量名、方法名、类名等:alt + shift + r
* 28.选中的结构的大小写的切换:变成大写: ctrl + shift + x
* 29.选中的结构的大小写的切换:变成小写:ctrl + shift + y
* 30.调出生成getter/setter/构造器等结构: alt + shift + s
* 31.显示当前选择资源(工程 or 文件)的属性:alt + enter
* 32.快速查找:参照选中的Word快速定位到下一个 :ctrl + k
*
* 33.关闭当前窗口:ctrl + w
* 34.关闭所有的窗口:ctrl + shift + w
* 35.查看指定的结构使用过的地方:ctrl + alt + g
* 36.查找与替换:ctrl + f
* 37.最大化当前的View:ctrl + m
* 38.直接定位到当前行的首位:home
* 39.直接定位到当前行的末位:end
.
.
.
1.1软件开发介绍
1.软件开发
软件,即一系列按照特定顺序组织的计算机数据和指令的集合。有系统软件和应用软件之分。
2.人机交互方式
(1)图形化界面(GUI):这种方式简单直观,使用者易于接受,容易上手操作。
(2)命令行方式(CLI):需要一个控制台,输入特定的指令,让计算机完成一些操作。较为麻烦,需要记住一些命令。
常用的DOS(CMD)命令:
dir:列出当前目录下的文件及文件夹
md:创建目录
rd:删除目录
cd:进入指定目录
cd…(两个点):退回到上一级目录
cd\:退回到根目录
del:删除文件
exit:推出dos命令行
补充:
echo(在文件夹中添加文件) 格式:echo giao>1.txt
echo:命令
giao:内容
1.txt:文件名(其中txt是文件格式)
常用快捷键
← →:移动光标
↑ ↓:掉阅历史操作命令
Delete和Backspace:删除字符
.
.
.
.
.
.
.
JAVA严格区分大小写
path环境变量的配置
1.为什么配置path环境变量?
path环境变量:Windows操作系统执行命令时所要搜索的路径
2.为什么要配置path
希望java的开发工具(java.exe,javac.exe)在任何的文件路径下都可以执行成功
不止是Java开发工具 只要是 .exe 的运行文件需要它在任何文件路径下执行成功都要配置path
java语言的特点:
1.面向对象性:
两个要素:类、对象
三个特征:封装、继承、多态
2.健壮性:
(1)去除了C语言中的指针
(2)自动的垃圾回收机制 → 仍然会出现内存溢出、内存泄漏问题
3.跨平台性:writ once,run anywhere:一次编译,到处运行(功劳归功于:JVM )
一.HelloJava
1.开发体验
步骤:
(1).将 Java 代码编写到扩展名为.java的文件中。
(2).通过 javac 命令对该 java 文件进行编译。
(3).通过 java 命令对生成的 class 文件进行运行。
Java文件 → javac.exe(编译) → .class文件 (一个或多个)→ java.exe(运行) → 结果
2.在一个java源文件中可以声明多个class。但是,只能最多有一个类声明为public而且要求声明为public的类名必须与源文件名相同。
3.程序的入口是main()方法。格式是固定的。
4.输出语句:
(1)System.out.println(); (先输出,后换行)
(2)System.out.print(); (输出不换行)
5.每一个执行语句都以 ; 结束。
6.编译的过程:编译以后,会生成一个或多个字节码文件(.class)字节码文件的文件名与java源文件中的类名相同。(写的代码有几个类就有几个字节码文件)
.
.
.
二.文档注释
1.单行注释 : //
2.多行注释 : /* */
3.文档注释 : /** 注释内容可以被JDK提供的 javadoc 所解析,生成一套以网页文件形式体现的该程序的说明文档。 */
操作指令:
javadoc -d mydoc -author -version HelloWorld.java
.
.
.
标识符号的使用
1.标识符:凡是自己可以起名字的地方都叫标识符。
比如:类名、变量名、方法名、接口名、包名...
2.标识符的命名规则:
由26个英文字母大小写,0~9, _ 或 $ 组成。
数字不可以开头。
不可以使用关键字和保留字,但能包含关键字和保留字。
Java中严格区分大小写,长度无限制。
标识符不能包含空格!
3.Java中的名称命名规范:
包名:多单词组成时所有字母都小写:xxxzzzyyy
类名:接口名:多单词组成时,所有单词的首字母大写:XxxZzzYyy(大驼峰命名法)
变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写:xxxZzzYzz(小驼峰命名法)
常量名:所有字母都大写。多单词时每个单词用下划线链接起来:XXX_ZZZ_YYY
4.注意:
(1):在起名字时,为了提高阅读性,要尽量有意义,“见名知意”。
(2):Java采用 Unicode 字符集,因此标识符也可以使用汉字声明,但是不建议使用。
5.转义字符:
(1)\b 退格符
(2)\n 换行符
(3)\r 回车符
(4)\t 制表符
(5)\" 双引号
(6)\' 单引号
(7)\\ 反斜线
.
.
.
.
.
三:变量与运算符
注:在java中整型常量默认类型为 int 型
注:在java中整浮点型常量默认类型为double型
变量
变量的概念:
- 内存中的一个存储区域。
- 该区域的数据可以在同一类型范围内不断变化。
- 变量是程序中最基本的储存单位。包含 变量类型、变量名的储存的值。
变量的使用:
- java定义变量的格式:数据类型 变量名 = 变量值;
变量的作用:
- 用在内存中保持数据。
使用变量注意:
- Java中每个变量必须先声明,后使用。
- 使用变量名来访问这块区域的数据。
- 变量的作用域:其定义所在的一对{}内。
- 变量只有在其作用域才有效。
- 同一个作用域内,不能定义名字相同的变量。
Java定义的数据类型
一、 变量按照数据类型来分:
1. 基本数据类型(primitve type):
- 数值型:
注:1MB = 1024KB ; 1KB = 1024B ; B = byte(字节) ? bit;
bit是计算机中最小的存储单位,byte是计算机中基本的存储单位;
整数类型(byte(1字节=8bit;取值:-128 ~ 127),short(2字节 ;取值:-2^15 ~ 2^15-1),int(4字节 ;取值:-2^31 ~ 2^31-1),long(8字节 ;取值:-2^63 ~ 2^63-1)):
(1)表示整数;
(2)一般Java程序中变量常用int型,除非不足以表示较大的数用long;
(3)声明long型的变量,变量值后必须要加 "L" ;
浮点类型(float,double)
(1)表示带小数点的数值;
(2)因为double的精度是float的两倍。通常采用此类型。
(3)声明float型的变量,变量值后必须要加 "F" ;
(3)两个浮点型表示数的范围大小比 整型中的Long还大 ,而内存比Long小;
- 字符型(char)
(1)char变量值只能写一个字符
(2)Unicode值可以用来表示字符型变量值
(3)转义字符:
char g3 = '\t';
System.out.print("hello"+g3);
System.out.println("java!");
输出:hello java!
- 布尔型(boolean)
只能取两个值:true false
常常在条件判断、循环结构中
2.引用数据类型(reference type):
- 类(class) 字符串在class里面
- 接口(interface)
- 数组([])
二、变量在类中声明的位置: (扩展:面向对象才说的内容)
在方法体外,类体内声明的变量成为:成员变量。
在方法体内,声明的变量成为:局部变量。
1.成员变量:
- 实例变量(不以static修饰)
- 类变量(以static修饰)
2.局部变量:
- 形参(方法、构造器中定义的变量)
- 方法局部变量(在方法内定义)
- 代码块局部变量(在代码块内定义)
注意:二者在初始化值方面的异同:
- 同:都有生命周期
- 异:局部变量除形参外,需显示初始化
基本数据类型之间的运算规则:
前提:这里讨论的只是七种基本数据类型变量间的运算。不包含boolean类型
1.自动类型提升:
- 结论:当容量小的数据类型的变量与容量大的数据类型做运算时,结果自动提示为容量大的类型。
- byte --> short --> int --> long --> float --> double
- 特别:当byte、short、char这三种数据类型的变量做运算时,结果为int型 ,就算是两个byte型的做运算都是int型。
- 书上的解释:java在做运算的时候,如果操作数均在 int 范围内,那么一律在 int 的空间内运算。
2.强制类型转换:
说明:此时的容量大小指,表示数的范围的大和小。比如:float容量要大于long的容量
注:对于byte/short/char三种类型来说,如果右侧赋值的数值没有超过范围,
那么 javac编译器 将会自动隐含的为我们补上一个 (byte) (short) (char) 。
一.编码情况
//编译成功 因为整数过小 系统把123123这个值当为int , 就相当于int型转long型这个过程
long L = 123123;
System.out.println(L);
//编译失败:过大的整数要加L
//long L1 = 123123123131;
long L1 = 123123123131L; //编译成功
System.out.println(L1);
//编译失败 浮点型float一定要加F
float f1 = 12.3;
System.out.println(f1);
二.编码情况2
byte b = 12;
// 这里的 1 看成一个常量,整型常量默认类型为int型
//byte b2 = b + 1; //编译失败
//这里的12.3看出一个常量,浮点型常量默认类型为double型
//float f1 = b + 12.3; //编译失败
.
String类型变量的使用 (它是引用数据类型)
String变量的使用
1.String属于引用数据类型,翻译为:字符串
2.声明 String 类型变量时,必须使用一对 "" 或者 值+"" 两种情况
3. String 可以和 8 种基本数据类型做运算,切运算只能是链接运算,链接符号为 + (boolean 也能做链接运算)
4.运算的结果仍然是 String 类型,前提是 + 左右两边有String类型的变量
5.例题:
char C = 'a';
int num = 10;
String str = "hello";
System.out.println(C+num+str); //107hello 因为:a = 97 先运算了C+num 这俩中没有String类型的所以是 97+10;
System.out.println(str+num+C); //hello10a 因为 先运算 str+num str为String类型的所以 str+num 的值为String类型(hello10),然后又是String类型+C 得到的也是String类型(hello10a);
.
.
.
进制
1.关于进制
-
对于整数,有4种表示方式:
二进制(binary):0、1 ,满2进1.以0b或0B开头。如:0b1100
十进制(decimal):0-9,满10进1。
八进制(octal):0-7,满8进1.以数字0开头表示。如:0123
十六进制(hex):0-9及A-F,满16进1.以0x或0X开头表示。此处A-F不区分大小写。
如:0x21AF+1=0X21B0 -
所有数字在计算机底层都以 二进制 的形式存在
二进制:
- Java整数常量默认是 int 类型,当用二进制定义整数时,其第32位(一共是32节,也就是存储的最后一节)是符号位;当是long类型时,二进制默认占64位(64节),第64位(最后一节)是符号位。
- 二进制的整数有如下三种形式:
1.原码:直接将一个数值转成二进制数。最高位是符号位:最高位是0为正数,1为负数。
2.负数的反码:是对原码按位取反(里面每一位或者说是每一节的 0变成1、1变成0 ),只是最高位(符号位)确定为 1 不变。
3.负数的补码:其反码+1。
如反码为:[1][0][1][0][0][1][0][1];(类似计算机底层存储,这是8位(8bit)就是 byte 类型的)
补码就为:[1][0][1][0][0][1][1][0];
计算机以二进制 补码 的形式保存所有的整数。
1.正数的原码、反码、补码都相同
2.负数的补码是上面介绍的其反码 +1 。
.
.
.
进制转换
1.二进制 转 十进制
如: 1 0 1 1 0 转 十进制
从右往左算: 一个值一个值的算最后加起来
第一个为2的0次方(2^0)特殊:它的值要么是1要么是0
以下前提对应的数是 1
第二个为2的1次方(2^1)
第三个为2的1次方(2^2)
第四个为2的1次方(2^3)
第五个为2的1次方(2^4)
开始算 :1 0 1 1 0
第一数个 0 :值为 0
第一数个 1 :值为 2
第一数个 1 :值为 4
第一数个 0 :值为 0
第一数个 1 :值为 16
相加: 0 + 2 + 4 + 0 +16 = 22
二进制:1 0 1 1 0 转 十进制为 22;
2.十进制 转 二进制:除二取余的逆
如: 十进制 13 转二进制
解释:拿13除以2,记下得到的余数,得到的商继续除2,继续记下余数,商再除2....
(1)13/2=6...1
(2)6/2=3...0
(3)3/2=1...1
(4)1/2=0...1
(5)0/2=0...0
(6)0/2=0...0
(N)0/2=0...0
取每次算完的余数:10110000...
在把得到的余数倒(逆)过来写:...0001101;
13转二进制的值就是:1101
.
.
.
运算符
1.运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等。
2.运算符分为:
(1)算数运算符:+ - * / % ++ --
注:
1.前++(先自增1,后运算)、后++(先运算后自增1)
自增自减不会改变数据类型!
2.连接符 + :只能使用在String与其他数据类型(包括String自身)变量之间使用。
% :取余运算
1.结果的符号与 被模数 的符号相同 !!!!
2.开发中,经常使用%来判断能否被除尽的情况
int m1 = 12;
int n1 = 5;
System.out.println("m1%n1="+m1%n1); //2
int m2 = -12;
int n2 = 5;
System.out.println("m2%n2="+m2%n2); //-2
int m3 = 12;
int n3 = -5;
System.out.println("m3%n3="+m3%n3); //2
int m4 = -12;
int n4 = -5;
System.out.println("m4%n4="+m4%n4); //-2
(2)赋值运算符:=(赋值) += -+ *= /= %=
注:+= 、 -= 这些虽然是在原值上加常量 但是和 num = num + 2 (数据类型在int以下或是整数与浮点数做运算编译不通过,因为改变数据类型了),这种方式也有不同, num+=2 这种方式不会改变 数据类型!!
==:对与引用数据类型来讲,比较的是两个引用数据类型变量的地址值是否相同
(3)比较运算符(关系运算符):== (恒等于) !=(不等于) >= <= > <
(4)逻辑运算符: 操作都是Boolean类型的变量,结果也是Boolean类型!
1. &、&&(与,and)
//区分& 与 &&
//相同点1:& 与 &&的运算结果相同
//相同点2:当符号左边是true时,二者都会执行符号右边的运算
//不同点:当符号左边是false时,& 会继续执行符号右边的运算,而 && 不再执行符号右边的运算
//开发中推荐使用 &&
boolean b1 = true;
b1 = false;
int num1 = 10;
if(b1 & (num1++>0)){
System.out.println("北京");
}else{
System.out.println("南京");
}
System.out.println("num1="+num1); //11 因为b1为false & 还要执行(num1++>0)
System.out.println();
boolean b2 = true;
b2 = false;
int num2 = 10;
if(b2 && (num2++>0)){
System.out.println("北京");
}else{
System.out.println("南京");
}
System.out.println("num1="+num2); //10 因为b1为false && 不往下执行(num1++>0)
2. |、 ||(或,or)
//区分:| 与 ||
//相同点1:| 与 || 的运算结果相同
//相同点2:相同点2:当符号左边是false时,二者都会执行符号右边的运算
//不同点:当符号左边是true时,| 会继续执行符号右边的运算,而 || 不再执行符号右边的运算
//开发中推荐使用 ||
boolean b3 = false;
b3 = true;
int num3 = 10;
if(b3 | (num3++>0)){
System.out.println("北京");
}else{
System.out.println("南京");
}
System.out.println("num3="+num3); //num3=11
System.out.println();
boolean b4 = false;
b4 = true;
int num4 = 10;
if(b4 || (num4++>0)){
System.out.println("北京");
}else{
System.out.println("南京");
}
System.out.println("num4="+num4); //num4=10
3. !(非)
4. ^(逻辑异或,两个都是true或false就是false,两个不一样就true)
(5)位运算符(用比较少):
·位运算符: 注意:没有<<<
1. << 左移:范例:3<<2=12 --> 3*2*2=12 ; 左移一位就乘2
·二进制表示:11 向左移两位 1100 (后面用0来补)所以得到的值就是 12。如果移的多了 最大位变成1了那这个数就是负数。
2. >> 右移:范例:3>>1=1 --> 3/2=1 ; 右移一位就除2
·二进制表示:11 向右移一位 1 (如果最大位是1,前面的值就用1来补,是0则用0来补)所以得到的值就是 1。如果最大位是1,那么补充最大的数也是1,当然这个数是负数。
3. >>> 无符号右移: 范例:3>>>1=1 --> 3/2=1
4. & 与运算: 6&3=2
·二进制表示 :把运算符 & 左右两边数用二进制表示出来,一个放上面一个放下面 对应的每一节中需要两个都是有1才写1,其中有0就写0,最后得出的二进制就是答案。
6的二进制:0 1 1 0
3的二进制:0 0 1 1
得到二进制:0 0 1 0 值:2
5. | 或运算: 6|3=7
·二进制表示 :把运算符 | 左右两边数用二进制表示出来,一个放上面一个放下面 对应的每一节中其中有一个是1就写1,要是都是0就写0,最后得出的二进制就是答案。
6的二进制:0 1 1 0
3的二进制:0 0 1 1
得到二进制:0 1 1 1 值:7
6. ^ 异或运算: 12^5 = 9
·二进制表示 :把运算符 ^ 左右两边数用二进制表示出来,一个放上面一个放下面 对应的每一节中两个数是一样的(两个都是0或者两个都是1)就写0,两个值都不一样就写1,最后得出的二进制就是答案。
12的二进制:1 1 0 0
5的二进制:0 1 0 1
得到二进制:1 0 0 1 值:9
7. ~ 取反:~6 = -7
正数取反,各二进制码按补码各取反
负数取反,各二进制码按补码各取反
二进制6:0000 0000 0000 0000 0000 0000 0000 0110
取反就是:1111 1111 1111 1111 1111 1111 1111 1001 值:-7
·位运算是直接对整数的二进制进行运算
(6)三元运算符:
1.格式:
· (条件表达式)?表达式1:表达式2; 注:条件表达式是boolean类型
括号条件表达式为true,运算后的结果是表达式1;
括号条件表达式为false,运算后的结果是表达式2;
· 表达式1和表达式2 必须为统一为一个类型!!(不能一个表达式是数字类型一个是字符串类型)
·三元运算符可以嵌套
m = 12;
n = 12;
String maxStr2 = (m>n)?"m大":((m == n)? "m和n一样大":"n大"); //三元运算符可以嵌套
System.out.println(maxStr2);
· 三元运算符与 if-else 的联系与区别:
(1)三元运算符可简化if-else语句
(2)三元运算符要求必须返回一个结果。
(3)if后的代码可有多个语句
· 改写if...else
(1)凡是可以使用三元运算符的地方,都可以改写为if...else
反之不成立(能用if...else写的不一定能改写为三元运算符 , 因为三元要求为统一类型)
(2)如果程序既可以使用三元运算符又可以使用if...else结构,我们优先选择三元运算符。原因:简洁,执行效率高;毕竟只是个运算符。
.
.
.
.
.
四:程序流程控制
- 流程控制语句是用来控制程序中各语句执行顺序的语句,可以把语句组合成能完成一定功能的小逻辑模块。
- 其流程控制方式采用结构化程序设计中规定的三种基本流程结构,即:
1.顺序结构:
(1)程序从上到下逐行的执行,中间没有任何判断和跳转
2.分支结构
(1)根据条件,选择性的执行某段代码。
(2)有if...else和switch-case两种分支语句。
3.循环结构
(1)根据循环条件,重复性的执行某段代码。
(2)有while、do...while、for三种循环语句。
(3)注:JDK1.5提供了foreach循环,方便的遍历集合、数组元素。
.
.
.
分支结构
1. if-else结构
三种结构:
第一种:
if(条件表达式){
执行表达式
}
第二种:二选一
if(条件表达式){
执行表达式1
}else{
执行表达式2
}
第三种:多选一
if(条件表达式){
执行表达式1
}else if{
执行表达式2
}else if{
执行表达式3
}
...
else{
执行表达式N
}
2.switch-case结构:
(1)凡是可以使用switch-case的结构都可以转换为if-else。反之,不成立。
(2)我们写分支机构时,当发现既可以使用switch-case,(同时,switch中表达式的取值情况不太多),又可以使用if-else时,我们优先使用switch-case。原因:switch-case执行效率高。
switch(表达式){
case 常量1:
执行语句1;
break;
case 常量2:
执行语句2;
break;
...
default:
执行语句n;
break; //可加可不加,反正都最后一个
说明:
①根据switch表达式中的值,依次匹配各个case中的常量。一旦匹配成功,则进入相应case结构中,调用其执行语句。
当调用完执行语句后,则仍然继续向下执行其他case结构中的执行语句,直到遇到break关键字或此switch-case结构
末尾结束为止(default)。
②break,可以在switch-case结构中,表示一旦执行到此关键字,就跳出switch-case结构。它是可选的,根据实际情况来看加不加。
③switch结构中的表达式,只能是如下6种类型之一:byte、short、char、int、枚举类型(JDK5.0新增)、String类型(JDK7.0新增)
④case之后只能声明常量。不能声明范围!!!!!!!!!!!!!!!!
⑤default:相当于if-else结构中的else
default结构是可选的,可加可不加,而且位置也是灵活的放哪都行,但放前面要加break。
⑥如果switch-case结构中的多个case的执行语句相同,则可以考虑进行合并
//大于60分及格,小于60分不及格
int score = 90;
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;
}
.
.
.
循环结构
1.循环语句的四个组成部分:
①初始化部分(init_statement) 。 类似于: i=0
②循环条件部分(test_exp)----布尔类型。 类似于: i<10
③循环体部分(body_statement)。 类似于:执行语句
④迭代部分(alter_statement) 。 类似于:i++
for 循环使用
循环结构的4个要素
①初始化部分(init_statement)
②循环条件部分(test_exp)
③循环体部分(body_statem
④迭代部分(alter_statement)
for循环结构:
for(①;②;④){
③
}
执行过程:① - ② - ③ - ④ - ② - ③ - ④ - ② - ③ - ④ ... - ②
int num = 1;
for(System.out.print('a');num < 3;System.out.print('b'),num++){
System.out.print('c');
}//根据执行过程 输出为:acbcb
break:一旦在循环中执行到break,就跳出循环
While 循环的使用
循环结构的4个要素
①初始化部分(init_statement)
②循环条件部分(test_exp)
③循环体部分(body_statem
④迭代部分(alter_statement)
While循环的结构
①
while(②){
③;
④;
}
执行过程:① - ② - ③ - ④ - ② - ③ - ④ - ② - ③ ... - ②
说明:
1.写while循环千万小心别丢了迭代条件。一旦丢了,就可能导致死循环!
2.写程序时,要避免出现思死循环。
3.for循环和while循环是可以相互转换的!(能用for就能用while,能用while就能用for)
do-While 循环的使用
循环结构的4个要素
①初始化部分(init_statement)
②循环条件部分(test_exp)
③循环体部分(body_statem
④迭代部分(alter_statement)
do-while循环结构:
①
do{
③;
④;
}while(②);
执行结构:① - ③ - ④ - ② - ③ - ④ - ② - ③ - ④ ... - ②
说明:
1.do-while循环至少会执行一次循环!
2.开发中,使用for和while更多,较少使用do-while
无限循环:
1.无限循环存在的原因是并不知道循环多少次,需要根据循环体内部某些条件,来控制循环结束。例如break
说明:
1.不在循环条件部分限制次数的结构:for(;;) 或 while(true)
2.结束循环有几种方式?
方式一:循环条件部分返回false
方式二:在循环体中,执行break
特殊关键字break和continue
break: swith-case、循环结构中
循环中使用的作用:结束当前循环,跳出当前循环结构
continue: 循环结构中
循环中使用的作用:结束当次循环,跳出当次循环
1.关键字后面不能声明执行语句
2.break和continue默认执行离此关键字最近的一层循环
注:如果要结束指定的循环需要在循环前加一个标识 例如:
big:for(int i = 1;i <= 4;i++){
for(int j = 1;j < 10;j++){
if(j%4==0){
// break big; //结束指定标识的一层循环结构
continue big; //结束指定标识的一层循环的当次循环
}
System.out.print(j);
}
System.out.println();
}
.
.
.
(提前遇到) 异常处理:
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。
异常发生的原因有很多,通常包含以下几大类:
1.用户输入了非法数据。
2.要打开的文件不存在。
3.网络通信时连接中断,或者JVM内存溢出。
这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。-
要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:
1.检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
2.运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
3.错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
Exception 类的层次
所有的异常类是从 java.lang.Exception 类继承的子类。
Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error 。
Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。
Error 用来指示运行时环境发生的错误。
例如,JVM 内存溢出。一般地,程序不会从错误中恢复。
异常类有两个主要的子类:IOException 类和 RuntimeException 类。
.
.
.
Java 内置异常类
Java 语言定义了一些异常类在 java.lang 标准包中。
标准运行时异常类的子类是最常见的异常类。由于 java.lang 包是默认加载到所有的 Java 程序的,所以大部分从运行时异常类继承而来的异常都可以直接使用。
Java 根据各个类库也定义了一些其他的异常:
下面的表中列出了 Java 的非检查性异常。(运行时异常)
异常 描述
ArithmeticException 当出现异常的运算条件时,抛出此异常。例如,一个整数"除以零"时,抛出此类的一个实例。
ArrayIndexOutOfBoundsException 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
ArrayStoreException 试图将错误类型的对象存储到一个对象数组时抛出的异常。
ClassCastException 当试图将对象强制转换为不是实例的子类时,抛出该异常。
IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数。
IllegalMonitorStateException 抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。
IllegalStateException 在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。
IllegalThreadStateException 线程没有处于请求操作所要求的适当状态时抛出的异常。
IndexOutOfBoundsException 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
NegativeArraySizeException 如果应用程序试图创建大小为负的数组,则抛出该异常。
NullPointerException 当应用程序试图在需要对象的地方使用 null 时,抛出该异常
NumberFormatException 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
SecurityException 由安全管理器抛出的异常,指示存在安全侵犯。
StringIndexOutOfBoundsException 此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。
UnsupportedOperationException 当不支持请求的操作时,抛出该异常。
下面的表中列出了 Java 定义在 java.lang 包中的检查性异常类。(编译时异常)
异常 描述
ClassNotFoundException 应用程序试图加载类时,找不到相应的类,抛出该异常。
CloneNotSupportedException 当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。
IllegalAccessException 拒绝访问一个类的时候,抛出该异常。
InstantiationException 当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。
InterruptedException 一个线程被另一个线程中断,抛出该异常。
NoSuchFieldException 请求的变量不存在
NoSuchMethodException 请求的方法不存在
异常方法
方法及说明
1 public String getMessage()
返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。
2 public Throwable getCause()
返回一个Throwable 对象代表异常原因。
3 public String toString()
使用getMessage()的结果返回类的串级名字。
4 public void printStackTrace()
打印toString()结果和栈层次到System.err,即错误输出流。
5 public StackTraceElement [] getStackTrace()
返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。
6 public Throwable fillInStackTrace()
用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。
.
.
.
使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。
try/catch代码块中的代码称为保护代码,使用 try/catch 的语法如下:
try
{
// 程序代码
}catch(ExceptionName e1)
{
//Catch 块
}
Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。
如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。
// 文件名 : ExcepTest.java
import java.io.*;
public class ExcepTest{
public static void main(String args[]){
try{
int a[] = new int[2];
System.out.println("Access element three :" + a[3]);
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("Exception thrown :" + e);
}
System.out.println("Out of the block");
}
}
//以上代码编译运行输出结果如下:
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block
多重捕获块:
一个 try 代码块后面跟随多个 catch 代码块的情况就叫多重捕获。
多重捕获块的语法如下所示:
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}catch(异常类型3 异常的变量名3){
// 程序代码
}
上面的代码段包含了 3 个 catch块。
可以在 try 语句后面添加任意数量的 catch 块。
如果保护代码中发生异常,异常被抛给第一个 catch 块。
如果抛出异常的数据类型与 ExceptionType1 匹配,它在这里就会被捕获。
如果不匹配,它会被传递给第二个 catch 块。
如此,直到异常被捕获或者通过所有的 catch 块。
.
.
.
throws/throw 关键字:
如果一个方法没有捕获到一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。
也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
import java.io.*;
public class className
{
public void deposit(double amount) throws RemoteException
{
// Method implementation
throw new RemoteException();
}
//Remainder of class definition
}
一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。
import java.io.*;
public class className
{
public void withdraw(double amount) throws RemoteException,
InsufficientFundsException
{
// Method implementation
}
//Remainder of class definition
}
.
.
.
finally关键字
finally 关键字用来创建在 try 代码块后面执行的代码块。
无论是否发生异常,finally 代码块中的代码总会被执行。
在 finally 代码块中,可以运行清理类型等收尾善后性质的语句。
finally 代码块出现在 catch 代码块最后,语法如下:
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}finally{
// 程序代码
}
public class ExcepTest{
public static void main(String args[]){
int a[] = new int[2];
try{
System.out.println("Access element three :" + a[3]);
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("Exception thrown :" + e);
}
finally{
a[0] = 6;
System.out.println("First element value: " +a[0]);
System.out.println("The finally statement is executed");
}
}
}
// 输出
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
First element value: 6
The finally statement is executed
.
.
.
数组(Array)
一.数组的概述:
1.数组的理解:
数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,
并通过编号的方式对这些数据进行统一管理。
2.数组相关的概念:
> 数组名
> 元素
> 下标(索引)
> 数组的长度,元素的个数
3.数组的特点:
1)数组是有序排列的
2)数组属于引用数据类型的变量。数组的元素,既可以是基本数据类型,也可以是引用数据类型
3)创建数组对象会在内存中开辟一整块连续的空间
4)数组的长度一旦确定就不能修改。
4.数组的分类:
① 按照维数:一维数组、二维数组、…
② 按照数组元素的类型:基本数据类型元素的数组、引用数据类型元素数组
内存的简化结构:
一维数组的使用:
1)一维数组的声明和初始化
> 静态初始化:数组的初始化和数组元素的赋值操作同时进行:
int[] ids = new int[]{1001,1002,1003,1004};
注:
静态初始化另一种写法:类型推断,必须声明初始化在同一行
int[] ids1 = {1001,1002,1003,1004};
错误写法:因为不在同一行
int[] ids2;
ids2 = {1001,1002,1003,1004};
> 动态初始化:数组的初始化和数组元素的赋值操作分开进行
String[] names = new String[5];
names[0] = "小明";
names[1] = "小花";
names[2] = "小龙";
names[3] = "小马";
names[4] = "大炮";
> 总结:数组一旦初始化完成,长度就确定了
2)如何调用数组的指定位置的元素
> 通过下标(索引)的方式调用
> 数组的下标(索引)从0开始的,到数组长度 -1 结束
3)如何获取数组的长度
> 获取数组长度 ------属性:length
4)如何遍历数组
for(int i = 0;i < arr.length;i++){
System.out.println(arr[i]);
}
5)数组元素的默认初始化值
> 数组元素是整形:0
> 数组元素是浮点型:0.0
> 数组元素是char型:0('0')或'\u0000',而非"0"
> 数组元素是boolean型:false
》数组元素是引用数据类型时:null
6)数组的内存解析:👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇
注:以上图还可知 如果输出一个数组的话 输出的结果就是该数组的地址!!!!!!!
一般数组地址值结构为:[[I@15db9742
[[ 代表二维数组(只一个 [ 就是一维数组,以此类推)
I 代表 int 型(以此类推:F代表 float型... String型会把包名全写出来)
二维数组:
1.理解:
我们可以看成是一维数组array1又作为另一个一维数组array2的元素而存在。(一个数组作为另外一个数组的元素而出现)
其实,从数底层的运行机制来看,其实没有多为数组(就像for循环的嵌套一样)
2.二维数组的使用:
1)一维数组的声明和初始化
① 静态初始化:
int[][] arr1 = new int[][]{{1,2,3},{4,5},{6,7,8}};
另一种写法:类型推断,必须声明初始化在同一行
int[][] arr11 = {{1,2,3},{4,5},{6,7,8}};
注意特殊写法情况:int[] x,y[]; x是一维数组,y是二维数组。Java中多维数组不必都是规则矩阵形式
② 动态:
动态初始化1:
String[][] arr2 = new String[3][2];
动态初始化2
int[][] arr3 = new int[3][];
2)如何调用数组的指定位置的元素
一:
int[][] arr1 = new int[][]{{1,2,3},{4,5},{6,7,8}};
System.out.println(arr1[0][1]); //2
二:
String[][] arr2 = new String[3][2];
System.out.println(arr2[1][1]); //null 没赋值就是默认的null
三(难点):
int[][] arr3 = new int[3][];
//System.out.println(arr3[1][0]); //报错
arr3[1] = new int[4]; //要给外层一维数组的元素重新赋值新的数组
System.out.println(arr3[1][0]);
3)如何获取数组的长度
int arr4[][] = new int[][]{{{1,2,3},{4,5,9,10},{6,7,8}};
System.out.println(arr4.length); // 3
System.out.println(arr4[0].length); // 3
System.out.println(arr4[1].length); // 4
System.out.println(arr4[2].length); // 3
4)如何遍历数组
int arr4[][] = new int[][]{{{1,2,3},{4,5,9,10},{6,7,8}};
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();
}
输出:
1 2 3
4 5 9 10
6 7 8
5)数组元素的默认初始化值x
规定:二维数组分为 外层数组元素 , 内层数组元素
int[][] arr = new int[4][3];
外层元素:arr[0],arr[1]...
内层元素:arr[0][0],arr[1][2]...
针对于初始化方式一:
如:int[][] arr = new int[4][3];
外层元素的初始值为(arr[0]):地址值( 因为元素是数组,数组放在栈里面指向的是它的地址值)
内层元素的初始值为(arr[0][0]):与一维数组初始化情况相同(取决于元素的数据类型)
针对于初始化方式二:
如:int[][] arr = new int[4][];
外层元素的初始值为(arr[0]):null (因为没有赋值的数组是引用类型,引用类型的初始值:null)
内层元素的初始值为(arr[0][0]):报错 要给外层一维数组的元素(内层一维数组)重新赋值新的数组
6)数组的内存解析:👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇
数组赋值:
int[] arr1 = new int[]{1,2,5,6,7};
int[] arr2 = arr1; //不能称做数组的 “复制”,只是把 arr1在堆空间中的数组地址值赋给了arr2,所以arr2也指向arr1创建的数组 相当于我们电脑的软件创建了一个快捷方式。
arr2[0] = 0; //这里把 arr2 这个数组的第一个值改为 0 !!!!!!
System.out.println(arr1[0]); //输出 0 因为声上面 arr2[0] 改为了 0 ,而 arr2 和 arr1 所指向的是同一个数组,所以 arr2[0] 改为0 arr1[0] 也就是 0 !!!!!!
而的数组复制为:
arr2 = new int[arr1.length];
for(int i = 0; i < arr2.length;i++){
arr2[i]=arr1[i];
}
数组的 反转 和 查找 (线性查找、二分法查找):代码写在:文件夹(Wk)> 包(An array of)> 类( ArrayTest2Dome)
二分法查找的前提是:数组必须有序。
.
.
算法介绍
.
.
内部排序方法性能比较:
.
.
Arrays工具类的使用
java.util.Arrays 类即为操作数组的工具类,包含了用来操作数组(比如排序和搜索)的各种方法。
常用的几个方法:
// 1.boolean equals(int[] a,int[] b):判断两个数组是否相等。
int[] arr1 = new int[]{1,2,3,4};
int[] arr2 = new int[]{1,2,3,4};
boolean isEquals = Arrays.equals(arr1, arr2);
System.out.println(isEquals); //true
//2.String toString(int[] a):输出数组信息。
int[] arr3 = new int[]{1,4,5,6,8,9};
String str = Arrays.toString(arr3);
System.out.println(str); //[1,4,5,6,8,9]
//3.void fill(int[] a,int val):将指定值填充到数组之中(替换数组中的值)。
int[] arr4 = new int[]{20,30,50,60};
Arrays.fill(arr4, 33);
System.out.println(Arrays.toString(arr4)); //[33,33,33,33]
//4.void sort(int[] a):对数组进行排序。
int[] arr5 = new int[]{9,8,6,7,2,3,1,4,5};
Arrays.sort(arr5);
System.out.println(Arrays.toString(arr5)); //[1,2,3,4,5,6,7,8,9]
//5.int binarySearch(int[] a,int key):二分查找(前提数组有序)。
int[] arr6 = new int[]{50,55,80,90,100,150,220,300};
int index = Arrays.binarySearch(arr6, 80); //获取索引值(如果返回负数就是没找到)
System.out.println(index); //2
.
.
数组中的常见异常
// 1.数组角标越界的异常:ArrayIndexOutOfBoundsExcetion
int[] arr = new int[]{1,2,3,4,5};
//情况一
for(int i = 0;i <= arr.length;i++){
System.out.println(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()); //要加toString()方法才是空指针,因为通过null去调一个方法是调不了的(不加方法就输出null)
.
.
.
面向对象
学习面向对象内容的三条主线
问题:面向对象的编程思想?
一、Java类及类的成员
二、面向对象的三大特征
三、其他关键字
.
.
面向过程(POP)与 面向对象(OOP):
二者都是一种思想,面向对象是相对面向过程而言的。
面向过程:强调的是功能行为,以函数为最小单位,考虑怎么做。
面向对象:将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。
举例:“人把大象装进冰箱“
面向过程:
① 把冰箱门打开
②抬起大象,塞进冰箱
③ 把冰箱门关闭
面向对象:
人{
打开(冰箱){
冰箱.开开();
}
抬起(大象){
大象.进入(冰箱);
}
关闭(冰箱){
冰箱.闭合();
}
}
冰箱{
开开(){
//代码....
}
闭合(){
//代码....
}
}
大象{
进入(冰箱){
//代码
}
}
.
.
面向对象的思想概念:程序员=从面向过程的执行者
转化成了面向对象的指挥者
面向对象程序设计的重点是
类的设计
类的设计,其实就是类的成员的设计
.
.
类和对象
类(Class)
和对象(Object)
是面向对象的核心概念。
类是对一类事物的描述,是
抽象的
、概念上的定义
对象是实际存在
的该类事物的每个个体,因而也成为实例(instance)
“万事万物皆为对象”
一:设计类,其实就是设计类的成员
常见的类的成员有:
属性:对应类中的成员变量
通常叫法:属性 = 成员变量 = field = 域、字段
行为:对应类中的成员方法
通常叫法:方法 = 成员方法 = 函数 = method
创建类的对象 = 类的实例化 = 实例化类
二:类和对象的使用(面向对象思想落地的实现):
1.创建类,设计类的成员
2.创建类的对象
3.通过"对象.属性" 和 "对象.方法" 调用对象的结构
三:如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static的)
意味着:如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值。
四:对象的内存解析
引用类型的变量,只可能存储两类值:null 或 地址值(含变量的类型)
五:对象数组的内存解析
.
.
类中属性(成员变量)的使用
属性(成员变量) VS 局部变量
1.相同点:
①定义变量的格式:数据类型 变量名 = 变量值
②先声明,后使用
③变量都有其对应的作用域
2.不同点:
①:
在类中声明的位置不同
成员变量:直接定义在类的一对{}内
局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部变量
②:
关于权限修饰符的不同
成员变量:可以在声明属性时,值明其权限,使用权限修饰符
常用的权限修饰符:private、public、缺省(默认的)、protected
③:
默认初始化值的情况
成员变量:类的属性,根据其类型,都用默认初始化值
局部变量:没有默认初始化值
意味着,我们在调用局部变量之前,一定要赋值
特别的:形参在调用时,我们赋值即可
④:
在内存中加载的位置
成员变量:加载到堆空间中 (非static)
局部变量:加载到栈空间中
.
.
类中方法的声明和使用
方法:描述类应该具有的功能。
比如 :
Math类:random()、max(int a,int b)...
Scanner类:nextXxx() ...
Arrays类:toString()、sort()、equals()...
1. 举例:
public void eat(){}
public void sleep(int hour){}
public String getName(){}
public String getNation(String nation){}
2. 方法的声明:权限修饰符 返回值类型 方法名(形参列表){
方法体
}
3. 说明:
①关于权限修饰符:
Java规定的4种权限修饰符:private、public、缺省(默认的)、protected -->详情在封装性的内容
②返回值类型:有返回值 VS没有返回值
情况一:
如果有返回值,则必须在方法声明时,指定返回值的类型。同时,方法中需要使用 return 关键字来返回指定类型的数据。
如果方法没有返回值,则方法声明时,使用void来表示。通常没有返回值的方法中,就不使用 return,用的话只是结束此方法的作用。
情况二:
我们定义方法该不该用返回值?
(1)题目要求
(2)凭经验:具体问题具体分析
③方法名:属于标识符,遵守标识符的规则规范,“见名知意”
④形参列表:方法可以声明 0 个或多个形参。
格式:数据类型1 形参1,数据类型2 形参2,...
我们定义方法时,该不该定义形参?
(1)题目要求
(2)凭经验:具体问题具体分析
⑤方法体:方法功能的体现
4. return关键字的使用:
(1)使用范围:使用在方法体中
(2)作用:
①结束方法
②针对于有返回值类型的方法,使用 "return 数据" 方法返回所需要的数据
(3)注意点:return关键字后面不可以声明执行语句。
5. 方法的使用中,可以调用当前类的属性(成员变量)或方法
特殊的:方法A中又调用了方法A:递归方法。
方法中不可以定义新方法
.
.
复习一:
1.面向对象思想编程内容的三条主线分别是什么?
① 类及类的成员:属性、方法、构造器;代码块、内部类
② 面向对象的三大特征:封装、继承、多态
③ 其他关键字:this、super、abstract、interface、static、final、package、import
2.对面向对象中类和对象的理解,并指出二者的关系?
类:抽象的、概念上的内容。
对象:实实在在存在的一个个体。(在内存中真正的创建一个对象,占据了内存的空间)。
对象是由类派生出来的(new出来)。
3.面向对象思想的体现一:类和对象的创建和执行操作有哪三步?
①创建类
②类的实例化(创建类的对象)
③调用对象的结构:"对象.属性" "对象.方法"
.
.
匿名对象的使用
1.理解:我们创建的对象,没有显示赋给一个变量名,即为匿名对象。
2.特征:匿名对象只能调用一次。
3.使用:
PhoneMall mall = new PhoneMall();
mall.show(new Phone());
.
.
.
方法的重载
重载的概念:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
1.定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
"两同一不同":
① 同一个类、相同方法名
② 参数列表不同(参数个数不同、参数类型不同)
2.举例:
Array类中重载的方法:sort()、 binarySearch()、copyof()...
3.判断是否是重载:
跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系!!!!
4.在通过对象时,如何确定某一个指定的方法:
方法名 ---> 参数列表
.
.
可变个数的形参
JavaSE 5.0 中提供了Varargs(variable number of arguments)机制,允许直接定义能和多个实参相匹配的形参。
从而,可以用一种更简单的方式,来传递个数可变的实参。
1.jdk 5.0新增的内容
2.具体使用:
① 可变个数形参的格式:数据类型 ... 变量名 ---> public void show(String ... strs)
② 当调用可变个数形参的方法时,传入的参数个数可以是:0个、1个、2个.....
③ 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
④ 可变个数形参的方法与本类中方法名相同,形参类型相同的数组之间不构成重载(其中只传入一个数组)。二者不能共存
⑤ 可变个数形参在方法的形参中必须声明在末尾(它的长度不确定)
⑥ 可变个数形参在方法的形参中,最多只能声明一个可变形参
.
.
方法参数的值传递机制 ###
方法的形参的传递机制:值传递
1.
形参:方法定义时,声明的小括号内的参数
实参:方法调用时,实际传递给形参的数据
2. 值传递机制:
如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值。
如果变量是引用数据类型,此时实参赋给形参的是实参真实存储的地址值。
基本数据类型的方法值传递
引用数据类型的方法值传递
.
.
递归方法的使用(了解)
1.递归方法:一个方法体内调用他自身。
2.方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
3.递归一定要向已知方向递归
,否则这种递归就变成了无穷递归,类似于死循环。
.
.
.
封装与隐藏
一:
高内聚 :类的内部数据操作细节自己完成,不允许外部干涉;
低耦合 :仅对外暴露少量的方法用于使用。
二:隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露 的暴露出来。这就是封装性的设计思想。
三:使用者对类内部定义的属性(对象的成员变量)的直接操作会导致数据的错误、混乱或安全性问题。
面向对象的特征一:封装与隐藏
一:问题的引入
当我们创建一个类的对象以后,我们可以通过"对象.属性"的方式,对对象的属性进行赋值。这里,赋值操作要受到
属性的数据类型和存储范围的制约。除此之外,没有其他制约条件。但在实际问题中,我们往往需要给属性赋值加入
额外的限制条件。因为这个限制条件不能在属性声明时体现,我们只能通过方法进行限制条件的添加。(比如setXxx())
同时我们需要避免用户再使用"对象.属性"的方式进行赋值。则需要将属性声明为私有的(private)
--> 此时,针对属性就体现了封装性
二:封装性的体现
我们将类的属性私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值。
1、隐藏一个类中不需要对外提供的实现细节;
2、使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作;
3、便于修改,增强代码的可维护性;
拓展:封装性的体现:(都是封装性的体现之一并不等于封装性)
① 如上
② 不对外暴露的私有的方法
③ 单例模式
...
三:封装性的体现,需要权限修饰符来配合。
1、Java规定的4种权限(从小到大排列):private、default、protected、public
2、4种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类
3、具体的,4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
修饰类的话(内部类除外),只能使用:缺省、public
总结封装性:Java提供了4种权限修饰符来修饰类及类的内部结构,限制类及内部类的内部结构在被调用时的可见性
大小。
.
.
.
类的结构之三:构造器(或构造方法、constructor)
一、构造器的作用:
1.构造器是用来创建一个对象的 !!!
2.初始化对象的信息(属性…)
二、说明:
1.如果没有显式的定义类的构造器的话(并不单指空参构造器:详情下面第4点),则系统默认提供一个空参的构造器
2.定义构造器的格式:权限修饰符 类名(形参列表){}
3.一个类中定义的多个构造器,彼此构成重载
4.一旦我们显式的定义了类的构造器之后,系统就不在提供默认的空参构造器
5.一个类中,至少会有一个构造器。
.
.
.
总结:属性赋值的先后顺序
① 默认初始化
② 显式初始化
③ 构造器中赋值(初始化) //前三个为初始化,赋值一次
④ 通过"对象.方法" 或 "对象.属性"的方式,赋值 //方法随便调用,修改属性
以上操作的先后顺序:① -> ② -> ③ -> ④
.
.
.
this 关键字使用
使用this访问属性和方法时,如果在本类中未找到,会从父类中查找
1.this可以用来修饰、调用:属性、方法、构造器
2.this修饰属性和方法:
this理解为: ① 当前对象(若定义的对象名为p1,则 this.age 相当与于 p1.age)
② 当前正在创建的对象 (构造器中使用的this)
2.1 在类的方法中,我们可以使用"this.属性" 或 "this.方法"的方式,来调用当前对象的属性或方法。但是
通常情况下,我们都选择省略"this."。特殊情况下,如果方法的形参和类的属性同名时,我们必须显式
的使用"this.变量"的方式,表明此变量是属性,而不是形参。
2.2 在类的构造器中,我们可以使用"this.属性" 或 "this.方法"的方式,来调用当前正在创建对象的属性或方法。
但是通常情况下,我们都选择省略"this."。特殊情况下,如果构造器的形参和类的属性同名时,我们必须显式
的使用"this.变量"的方式,表明此变量是属性,而不是形参。
3.this调用构造器
① 我们在类的构造器中,可以显式的使用"this(形参列表)"的方式,调用本类中指定的其他构造器
② 构造器中不能通过"this(形参列表)"方式调用自己
③ 如果一个类中有n个构造器,则最多只能有 n - 1个构造器中使用"this(形参列表)"
注意!其中不能:构造器A 调 构造器B,构造器B 再调 构造器A;等等 这种类型的使用都会陷入一个死循环
④ 规定:"this(形参列表)"必须声明在当前构造器的首行
⑤ 构造器内部,最多只能声明一个"this(形参列表)"。 可以设置调用的那个构造器再调用其他构造器
.
.
.
关键字:package、import的使用
一:package关键字的使用
1.为了更好的实现项目中类的管理,提供包的概念
2.使用package声明类或接口所属的包
3.包,属于标识符,遵循标识符的命名规则、规范(zzzxxxyyy)、"见名知意"
4.每"."一次,就代表一层文件目录
补充:同一个包下,不能命名同名的接口、类。
不同的包,可以命名同名的接口、类
二:import关键字的使用
import:导入
1.在源文件中显式的使用import结构导入指定包下的类、接口
2.声明在包的声明和类的声明之间
3.如果需要导入多个结构,则并列写出即可
4.可以使用"xxx.*"的方式,表示可以导入xxx包下的所有结构(类、接口)
5.如果使用的类、接口是java.lang包下定义的,则可以省略import结构(例如:String、System)
6.如果使用的类或接口是本包下定义的,则也可以省略import结构
7.如果在源文件中,使用了不同包下的同名的类,则必须有一个类需要以全类名的方式显式
全类名写法:java.sql.Data data = new java.sql.Data(231225L);
8.如果使用"xxx.*"的方式表明可以调用xxx包下的所有结构。但如果使用xxx子包下的结构,则仍需要显式导入包
9.import static:导入指定类或接口中的静态结构:属性或方法。
例如:
System.out.println("hello"); ------->out.println("hello");
Math.random(); ------->random();
1. java.lang----包含一些Java语言的核心类,如String、Math、Integer、 System和Thread,提供常用功能
2. java.net----包含执行与网络相关的操作的类和接口。
3. java.io ----包含能提供多种输入/输出功能的类。
4. java.util----包含一些实用工具类,如定义系统特性、接口的集合框架类、使用与日期日历相关的函数。
5. java.text----包含了一些java格式化相关的类
6. java.sql----包含了java进行JDBC数据库编程的相关类/接口
7. java.awt----包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)。
.
.
.
继承性
一:继承性的好处:
①减少了代码的冗余,提高了代码的复用性
②便于功能的扩展
③为之后多态性的使用,提供前提
二:继承性的格式:class A extends B{}
A:子类、派生类、subclass
B:父类、超类、基类、superclass
①体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明所有的属性和方法。
②父类中声明为private的属性和方法,子类继承父类以后,任然认为获取了父类中
私有的结构。只是因为封装性的影响使得子类不能直接调用父类的结构而已。
总结就是:封装性和继承性"各司其职"
③子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。
extends:延申、扩展
三:Java中关于继承性的规定
①一个类可以被多个子类继承。
②Java中类的单继承性:一个类只能有一个父类
③子父类是相对概念。子类直接继承的父类称为:直接父类。间接继承的父类称为:间接父类
④子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
.
.
Object类理解
1.如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类
2.所有的java类(除了java.lang.Object类)都直接或间接的继承于java.lang.Object类
3.意味着,所有的java类具有java.lang.Object类声明的功能。
.
.
方法的重写(override/overwrite)
1.重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作
2.应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
3.重写的规定:
方法的声明:权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{
//方法体
}
约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
① 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
>特殊情况:子类不能重写父类中声明为 private 权限的方法
③ 返回值类型:
>父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型必须是void
>父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是 A类 或 A类的子类
>父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须是相同的基本数据类型
④ 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
注:
子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。
非static才能重写!!!
.
.
super关键字的使用
1.super理解为:父类的
2.super可以用来调用属性、方法、构造器
3.super的使用
① 我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式调用
父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
② 特殊情况:当子类和父类中定义了同名属性时,我们想在子类中调用父类中声明的属性,则必须
显式的使用"super.属性"的方式,表明调用的是父类中声明的属性。
③ 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,
则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
4.super调用构造器
① 我们可以在子类构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
② "super(形参列表)"的使用,必须声明在子类构造器的首行!
③ 我们在类的构造器中 只能出现"this.(形参列表)" 或 "super(形参列表)" 二选一!
④ 在构造器的首行,没有显式的声明"this.(形参列表)" 或 "super(形参列表)",则默认调用的是父类
中空参的构造器:super();
⑤ 在类的多个构造器中,至少有一个类的构造器中使用了"super(形参列表)",调用父类中的构造器
this和super的区别
.
.
子类对象实例化全过程
1.从结果上来看:(继承性)
子类继承父类以后,就获取了父类声明的属性、方法
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
2.从过程上来看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器...
直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类的
结构,子类对象才可以考虑进行调用。
明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
.
.
.
多态性
1.理解多态性:可以理解为一个事务的多种形态。
2.何为多态性:
对象的多态性:父类的引用指向子类的对象(子类的对象赋给父类的引用)
子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)。
Person p2 = new Man();
Person p3 = new Woman();
3.多态的使用:虚拟方法调用
Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。
(有了对象的多态性以后,我们在编译期,只能调用父类声明的方法,但在运行期,我们实际执行的是子类重写父类的方法)
简称:编译时,看左边;运行时,看右边。
“看左边”:看的是父类的引用(父类中不具备子类特有的方法)
“看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)
一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法。
因为编译时是父类类型,没有子类的成员变量(除重写父类方法外),因而编译错误。
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,
动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的,多态性是一个运行时行为。
4.多态性的使用前提:
① 类的继承关系
② 方法的重写
5.对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
① 若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的
同名方法,系统将不可能把父类里的方法转移到子类中。
② 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的
实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量
多态小结:
多态作用:
提高了代码的通用性,常称作接口重用
前提:
需要存在继承或者实现关系
有方法的重写
成员方法:
编译时:要查看引用变量所声明的类中是否有所调用的方法。
运行时:调用实际new的对象所属的类中的重写方法。
成员变量:
不具备多态性,只看引用变量所声明的类。
对象类型转换 (Casting ):
一、基本数据类型的Casting:
① 自动类型转换:小的数据类型可以自动转换成大的数据类型
如long g=20; double d=12.0f
② 强制类型转换:可以把大的数据类型强制转换(casting)成小的数据类型
如 float f=(float)12.0; int a=(int)1200L
二、对Java对象的强制类型转换称为转换
① 从子类到父类的类型转换可以自动进行
② 从父类到子类的类型转换必须通过造型(强制类型转换)实现
③ 无继承关系的引用类型间的转换是非法的
④ 在转换(强制类型转换)前可以使用instanceof操作符测试一个对象的类型
instanceof:
a instanceof A:判断对象a是否是类A的实例。如果是,返回ture;如果不是,返回false
使用前提:为了避免在向下转型时出现ClassCastException异常,我们在向下转型之前,先
进行instanceof的判断,返回ture,就进行向下转型。返回false,不进行向下转型
如果a instanceof A 返回ture,则 a instanceof B 也返回ture。
其中,B类是A类的父类
.
.
.
Object类的使用
java.lang.Object类
1.Object类是所有Java类的根父类
2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
3.Object类中的功能(属性、方法)就具有通用性。
属性:无
方法:equals() / toString() / getClass() / hashCode / clone() / finalize()
wait() 、 notify() 、 notifyAll
4.Object类只声明了一个空参构造器
.
.
== 和 equals() 区别
==:
1. 如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等
2. 如果比较的是引用数据类型变量:比较的是两个引用数据类型变量的地址值是否相同
equals()方法使用:
1. 是一个方法,而非运算符
2. 只能适用于引用数据类型
3. Object类中定义equals()的方法和 == 的作用是相同的(比较的是两个引用数据类型变量的地址值是否相同);
4. 像String、Data、File、包装类都重写了Object类中的equals()方法。重写以后,
比较的不是两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同。
5. 通常情况下,我们自定义的类如果使用equals()的话,也通常比较两个对象的"实体内容"是否相同。
那么,我们就需要对Object类中的equals()的方法进行重写
重写的原则:比较两个对象的实体内容是否相对
.
.
Java中的JUnit单元测试
步骤:
1. 选中当前工程 - 鼠标右键 - build path - add libraries - JUnit 4 - 下一步
2. 创建Java类,进行单元测试
此时的Java类要求:
① 此类是public的
② 此类提供公共无参的构造器
3. 此类声明单元测试方法
此时的单元测试方法:方法权限是public,没有返回值,没有形参
4. 单元测试方法上需要声明:@Test,并在单元测试类中导入
5. 声明好单元测试方法以后,就可以在方法体内测试相关的代码
① 写完代码后,双击方法名,右键:Run As - JUnit Test
说明:
1.如果执行结果没有任何异常:绿条
2.如果执行结构出现异常:红条
.
.
包装类的使用:
1. java提供了8种基本数据类型对应的包装类,为了使得基本数据类型的变量具有类的特征
2. 掌握:基本数据类型、包装类、String三者之间的相互转换
JDK 2.0新特性:自动装箱与自动拆箱
//自动装箱:
int num = 10;
Integer in = num;//自动装箱
//自动拆箱
Integer in1 = new Integer(12);
int num1 = in1;
基本数据类型、包装类 ----> String类型:调用String重载的valueOf(Xxx xxx)
String类型 ----> 基本数据类型、包装类:调用包装类的parseXxx()
.
.
.
static关键字
如果想让一个类的所有实例共享一个数据,就设置为静态变量(静态方法)!
1、static:静态的
2、static可以用来修饰:属性、方法、代码块、内部类
3、使用static修饰属性:静态变量(或类变量)
3.1 属性是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量)
实例变量:我们创建了类的多个对象,每个对象都有独立的拥有一套类中的非静态属性(实例变量)。
当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。
静态变量:我们创建了类的多个对象,每个对象共享同一个静态变量,当通过某个对象修改静态变量时,
会导致其他对象调用此静态变量时,是修改后的值。
3.2 static修饰属性的其他说明:
① 静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用
② 静态变量的加载要早于对象的创建。
③ 由于类只加载一次,则静态变量在内存中也只会存在一份:存在 方法区的静态域中。
④(是否可以调用) 静态变量 非静态变量
类 yes no
对象 yes yes
③ 静态属性举例:System.out; Math.PI;
4、使用static修饰方法:静态方法
① 随着类的加载而加载,可以通过"类.静态方法"的方式进行调用
② (是否可以调用) 静态方法 非静态方法
类 yes no
对象 yes yes
③ 静态方法中,只能调用静态的方法或属性
非静态方法中,即可以调用非静态的方法或属性,也可以调用静态的方法或属性
④ static方法不能被重写
5、static注意点:
5.1 在静态方法内,不能使用this关键字、super关键字
5.2 关于静态属性和静态方法的使用,都从生命周期的角度去理解。
6.static什么时候使用?
开发中,如何确定一个属性是否声明为static的?
> 属性是可以被多个对象共享的,不会随着对象的不同而不同的。
> 类中的常量也常常声明为static
开发中,如何确定一个方法是否声明为static的?
> 操作静态属性的方法,通常设置为static的
> 工具类中的方法,习惯上声明为static的。比如:Math、Arrays、Collections
.
.
.
设计模式:是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。
单例 (Singleton)设计模式(设计模式中的一种)
1.所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。
2.如何实现?
饿汉式 vs 懒汉式
饿汉式:上来就new好对象
懒汉式:用的时候才new对象
3.区分饿汉式和懒汉式
饿汉式
坏处:对象加载时间过长。
好处:饿汉式是线程安全的。
懒汉式
好处:延迟对象的创建。
坏处:目前写法线程不安全。-->多线程内容时修改
.
.
类的成员之四:代码块(或初始化块):
1. 代码块的作用:用来初始化类、对象(举例一:有一些属性需要添加一些方法、判断、异常什么的才能赋值,又要加载类时就得赋值,所以就会使用代码块)
2. 要修饰代码块只能用static修饰
3. 分类:静态代码块 vs 非静态代码块
4.静态代码块
>随着类的加载而执行,且只执行一次
>如果一个类中定义了多个静态代码块,则按照声明先后顺序执行(没必要写多个)
>静态代码块的执行要优先与非静态代码块的执行(因为随着类的加载而执行)
>作用:初始化类的信息
5.非静态代码块
>随着对象的创建而执行,每创建一个对象就执行一次
>如果一个类中定义了多个非静态代码块,则按照声明先后顺序执行(没必要写多个)
>作用:可以在创建对象时,对对象的属性等进行初始化
静态代码块和非静态代码块不是因为加载而加载了因为他们没有名所以直接执行了
.
.
.
final(最终的)
1. final可以用来修饰的结构:类、方法、变量
2. final用来修饰一个类:此类不能被其他类所继承。
比如:String类、System类、StringBuffer类
3. final用来修饰方法:此方法不能被重写
比如:Object类中getClass()
4. final用来修饰变量:此时的"变量"就称为是一个常量
4.1 final修饰属性(成员变量):
可以考虑赋值的位置有:显式初始化、代码块中初始化
4.2 final修饰局部变量
尤其是用final修饰形参时,表明此常量是一个常量。当我们调用此方法时,给常量形参赋一个实参。
一旦赋值以后,就只能在方法体内使用此形参,但不能进行赋值。
static final 用来修饰属性:全局常量
.
.
.
抽象类:abstract
1. abstract:抽象的
2. abstract可以用来修饰的结构:类、方法
3. abstract修饰类:抽象类
> 此类不能实例化
> 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化过程)
> 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关操作
4. abstract修饰方法:抽象方法
> 抽象方法只有方法的声明,没有方法体
> 包含抽象方法的类,一定是一个抽象类;抽象类中是可以没有抽象方法的
> 若子类重写了父类中的所有的抽象方法后,此子类才能实例化
若子类没有重写完所有父类中的抽象方法后,则此类也得修饰为一个抽象类
5.abstract使用上的注意点:
① abstract不能用来修饰:属性、构造器等结构
② abstract不能用来修饰私有方法、静态方法(静态方法不能重写)、final的方法(final方法不能重写)\final类(final类不能继承)。
.
.
.
接口
定义接口:interface
实现接口:implements
接口的使用
1. 接口使用interface来定义
2. Java中,接口和类是并列的两个结构
3. 如何定义接口:定义接口中的成员
3.1 JDK7以及以前:只能定义全局常量和抽象方法
> 全局常量:public static final的
> 抽象方法:public abstract的
3.2 JDK8:除了定义全局常量和方法之外,还可以定义 静态方法、默认方法 !!!!!!!!!!!
> 静态方法要显式使用static修饰
> 接口中定义的静态方法,只能通过接口来调用(接口名.静态方法)
> 默认方法要显式使用default关键字修饰,可以通过实现类对象来调用
> 默认方法任然可以通过实现类重写
> 如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,
那么子类再没有重写此方法的情况下默认调用的是父类中的方法。---> 类优先原则(只针对方法)
> 如果实现类实现了多个接口,而多个接口中实现了同名同参数的 默认方法 ,
那么再实现类没有重写此方法的情况下,报错。---> 接口冲突
虽然冲突但硬要实现这两个接口我们就必须重写此方法,或继承一个有和接口一样的方法的父类(就继承父类中的方法)--> 类优先原则
> 重写默认方法后如何调用父类、接口方法?
super.方法名(); 调用父类的方法
接口名.super.方法名(); 调用接口的方法
4. 接口中不能定义构造器!意味接口不能实例化
5. Java开发中,接口通过类去实现(implements)的方法来使用
如果实现类实现了接口中的所有抽象方法,则此实现类就可以实例化
如果实现类没有实现接口中所有抽象方法,则此实现类任要修饰为一个抽象类
6. Java类可以实现多个接口 ---> 弥补了Java单继承性的局限性
格式:class AA extends BB implements CC,DD,EE{}
7. 接口与接口直接可以继承,而且可以多继承
格式:interface CC extends AA,BB
**************************************************
8. 接口的具体使用,体现了多态性
9. 接口,实际上可以看做是一种规范
继承是一个"是不是"的关系,而接口实现则是 "能不能"的关系。
W 接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。
.
.
.
类的内部成员之五:内部类
1. Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
2. 内部类的分类:成员内部类(静态、非静态) vs 局部内部类(方法内、代码块内、构造器内)
3. 成员内部类:
一方面,作为外部类的成员:
> 调用外部类的结构(前面省略的不是"this."而是"外部类名.this.")
> 可以被static修饰
> 可以被4种不同的权限修饰
另一方面,作为一个类:
> 类内可以定义属性、方法、构造器等
> 可以被final修饰,表示此类不能被继承。不修饰可以被继承
> 可以被abstract修饰
4. 关注如下3个问题
① 如何实例化成员内部类的对象
② 如何在成员内部类中区分调用外部类的结构
③ 开发中局部内部类的使用,见:InnerClassTest1
.
.
.
异常
Java程序在执行过程中所发生的异常事件可分为两类:
Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError和OOM。
一般不编写针对性的代码进行处理。!!!!!
Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。
例如:
空指针访问
试图读取不存在的文件
网络连接中断
数组角标越界
异常主要学习Exception
.
.
Exception异常体系结构
java.lang.Throwable
java.lang.Error:一般不编写针对性的处理代码进行处理
java.lang.Exception:可以进行异常的处理 (以下为常见的一些异常类)
编译时异常(checked)
IOException
FileNotFoundException
ClassNotFoundException
运行时异常(unchecked)
NullPointerException
ArrayIndexOutOfBoundsException
ClassCastException
NumberFormatException
InputMismatchException
ArithmeticException
Java 的非检查性异常。(运行时异常)
异常 描述
ArithmeticException 当出现异常的运算条件时,抛出此异常。例如,一个整数"除以零"时,抛出此类的一个实例。
ArrayIndexOutOfBoundsException 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
ArrayStoreException 试图将错误类型的对象存储到一个对象数组时抛出的异常。
ClassCastException 当试图将对象强制转换为不是实例的子类时,抛出该异常。
IllegalArgumentException 抛出的异常表明向方法传递了一个不合法或不正确的参数。
IllegalMonitorStateException 抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。
IllegalStateException 在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。
IllegalThreadStateException 线程没有处于请求操作所要求的适当状态时抛出的异常。
IndexOutOfBoundsException 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
NegativeArraySizeException 如果应用程序试图创建大小为负的数组,则抛出该异常。
NullPointerException 当应用程序试图在需要对象的地方使用 null 时,抛出该异常
NumberFormatException 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
SecurityException 由安全管理器抛出的异常,指示存在安全侵犯。
StringIndexOutOfBoundsException 此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。
UnsupportedOperationException 当不支持请求的操作时,抛出该异常。
Java 定义在 java.lang 包中的检查性异常类。(编译时异常)
异常 描述
ClassNotFoundException 应用程序试图加载类时,找不到相应的类,抛出该异常。
CloneNotSupportedException 当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。
IllegalAccessException 拒绝访问一个类的时候,抛出该异常。
InstantiationException 当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。
InterruptedException 一个线程被另一个线程中断,抛出该异常。
NoSuchFieldException 请求的变量不存在
NoSuchMethodException 请求的方法不存在
.
.
Exception异常的处理:抓抛模型
过程一:
"抛": 程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常的对象将此对象抛出。
一旦抛出对象以后,其后面的代码就不在执行。(默认出现异常的情况)
关于异常对象的产生:
① 系统自动生成的异常对象
② 手动的生成一个异常对象,并抛出(throw)
格式:throw new 异常类型();
过程二:"抓": 可以理解为异常的处理方式:① try-catch-finally ② throws
异常处理方式一、try-catch-finally的使用
结构:
try{
//可能出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量2){
//处理异常的方式2
}catch(异常类型3 变量3){
//处理异常的方式3
}
...
finally{
//一定会执行的代码
}
说明:
1. finally是可选的。
2. 使用try将可能出现异常的代码包装起来,在执行过程中一旦出现异常,就会生成一个对应异常类的对象,
根据此对象的类型,去catch中进行匹配。
3. 一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出
当前的try-catch结构(在没有写finally情况下)。继续执行其后面的代码。
4. catch中的异常类型如果没有子父类关系,则谁声明在前,谁声明在后无所谓。
catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类前面。否则,报错。
5. 常用的异常对象处理的方式:① e.getMessage() ② e.printStackTrace()
6. 在try结构中声明的变量,在出了try结构以后,就不能再被调用。(可以声明在外面)
7. try-catch-finally 结构可以嵌套。
体会1:使用 try-catch-finally 处理编译时异常,使得程序在编译时不再报错,但是运行时仍可能报错。
相当于我们使用 try-catch-finally 将一个编译时可能出现的异常,延迟到运行时出现。
体会2: 开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。
针对编译时异常,我们一定要考虑异常的处理。允许忽略运行时异常。
异常处理的方式二:throws + 异常类型
1. "throws + 异常类型" 写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足
throws后异常类型时,就会被抛出。此方法中异常代码后续的代码也不再执行!
2.体会:
try-catch-finally:真正的将异常处理掉了。
throws的方式只是将异常抛给了方法的调用者。并没有真正将异常处理掉。
3. 开发中如何选择try-catch-finally 还是使用 throws?
① 如果父类中被重写的方法没有 throws 方式处理异常,则子类重写的方法也不能使用 throws,意味着
如果子类重写的方法中有异常,必须使用 try-catch-finally 方式处理。
② 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法
使用 throws 的方式进行处理。而执行的方法a可以考虑使用 try-catch-finally 的方式进行处理。
原因:
因为执行的几个方法是递进的关系如果每个方法自己使用 try-catch-finally 处理则如果真有异常
返回给另外方法的值就会不正确,就没有意义。所以我们把所有递进的方法统一到调用的时候使用
try-catch-finally 的方式处理,这样如果一个方法异常就直接进入catch,之后的方法也就不用
再执行下去了
.
.
如何自定义异常类?
1. 继承于现有的异常结构:RuntimeException、Exception
2. 提供全局常量:serialVersionUID
3. 提供重载的构造器
.
.
Exception中常用的方法
1 public String getMessage()
返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。
2 public Throwable getCause()
返回一个Throwable 对象代表异常原因。
3 public String toString()
使用getMessage()的结果返回类的串级名字。
4 public void printStackTrace()
打印toString()结果和栈层次到System.err,即错误输出流。
5 public StackTraceElement [] getStackTrace()
返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。
6 public Throwable fillInStackTrace()
用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。
.
.
通常,Java的异常(Throwable)分为可查的异常(checked exceptions)和不可查的异常(unchecked exceptions)。
1. 可查异常(编译器要求必须处置的异常):正确的程序在运行中,很容易出现的、情理可容的异常状况。除了Exception中的RuntimeException及RuntimeException的子类以外,其他的Exception类及其子类(例如:IOException和ClassNotFoundException)都属于可查异常。这种异常的特点是Java编译器会检查它,也就是说,当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,否则编译不会通过。
2. 不可查异常(编译器不要求强制处置的异常):包括运行时异常(RuntimeException与其子类)和错误(Error)。RuntimeException表示编译器不会检查程序是否对RuntimeException作了处理,在程序中不必捕获RuntimException类型的异常,也不必在方法体声明抛出RuntimeException类。RuntimeException发生的时候,表示程序中出现了编程错误,所以应该找出错误修改程序,而不是去捕获RuntimeException。
.
.
总结:Java规定:对于可查异常必须捕捉、或者声明抛出。允许忽略不可查的RuntimeException和Error。
.
.
相关的问题
1. 为什么要创建自己的异常?
答:当Java内置的异常都不能明确的说明异常情况的时候,需要创建自己的异常。
2. 应该在声明方法抛出异常还是在方法中捕获异常?
答:捕捉并处理知道如何处理的异常,而抛出不知道如何处理的异常。
.
.
.
.
.
.
多线程
基本概念:程序、进程、线程
进程可以细分为多个线程。
每个线程,拥有自己独立的:栈、程序计算器
多个线程,共享同一个进程中的结构:方法区、堆
(线程的通信)
.
.
.
多线程的创建
方式一:继承Thread类
1.创建一个继承Thread类的子类
2.重写Thread类的run()方法 --> 将此线程执行的操作声明在run()方法中
3.创建Thread类的子类的对象 --> 一个对象就相当于一个线程,要想创建多线程就创建多个对象
4.通过此对象调用start()方法 --> 作用:①启动当前线程 ②调用当前线程的run()方法
问题一:我们不能通过直接调用run()的方式启动线程,这只是普通的调用对象的方法。
问题二:若需要在创建一个线程不可以让已经使用start()方法的线程再次调用start()方法,需要重新创建一个线程的对象。
方式二:实现Runnable接口
1. 创建一个实现了Runnable接口的类
2. 实现类去实现Runnable中的抽象方法:run()
3. 创建实现类的对象
4. 创建Thread类的对象,将Runnable接口的实现类的对象作为参数传递到Thread类的构造器中
5. 通过Thread类的对象调用start()
这里的 start() 中调用当前线程的run()-->调用了Thread构造器中Runnable类型的参数target的run()方法。具体看run()的源码
比较创建线程的两种方式(Thread类 和 Runnable接口):
开发中:优先选择 --> 实现Runnable接口的方式
原因:
① 实现的方式没有类的单继承局限性
② 实现的方式更适合处理多个线程有共享数据的情况
两者的联系:public class Thread implements Runnable --> Thread类也是Runnable的实现类。
相同点:两种方式都要重写run(),将线程要执行的逻辑声明在run()中。
方式三:实现Callable接口。 --- JDK 5.0新曾
1.创建一个实现Callable接口的实现类。
2.实现call方法,将此线程需要执行的操作声明在call()方法中。
3.创建Callble接口实现类的对象。
4.创建FutureTask类的对象,将Callble接口实现类的对象作为参数传递到FutureTask对象的构造器中。
5.创建Thread类的对象,将FutureTask类的对象作为参数传递到Thread对象的构造器中,并调用start(),启动线程。
6.获取Callable中call方法的返回值 ---> FutureTask类的对象.get();(不在线程中,是线程结束后的返回值)
如何理解实现Callable接口比实现Runnable接口创建多线程强大?
1. call()可以有返回值。
2. call()可以抛出异常,被外面的操作捕获,从而获取异常信息。
3. Callable接口支持泛型
FutureTask类 是 Callable接口和Runnable接口的实现类!
开发中大部分使用线程池!
方式四:使用线程池
JDK 5.0起提供了线程池相关API:ExecutorService 和 Executors。
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor。
开启线程方法一:void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable接口的线程
开启线程方法二:<T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般用来执行Callable接口的线程
结束线程:void shutdown()
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池。
Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池;
Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池 (普通方法,通常使用这个);
Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池;
Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行;
好处:
1.提高响应速度(减少了创建新线程的时间)
2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
3.便于线程管理
setcorePoolSize:核心池的大小
setmaximunPoolSize:最大线程数
setkeepAliveTime:线程没有任务时最多保持多长时间后会终止
.
.
测试Thread中的常用方法
1. start():启动当前线程;调用当前线程的run()
2. run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
3. currentThread:静态方法,返回当前代码执行的线程
4. getName():获取当前线程的名字
5. setName()/调用父类带参构造器(public Thread(String name){}):设置当前线程的名字
6. yield():释放当前cpu的执行权(但也有可能在下一刻又分配到当前线程),如果队列中没有同优先级的线程,忽略此方法。
7. join():在 线程A 中调用 线程B 的join(),此时 线程A 就进入阻塞状态,直到 线程B 完全执行完以后,线程A 才结束阻塞状态。
8. stop():已过时。当执行此方法时,强制结束当前进程。
9. sleep(long millitime):让当前线程“睡眠”(阻塞)指定的millitime毫秒。在指定的毫秒时间内,当前线程为阻塞状态。
10. isAlive():判断当前线程是否存活(true/false)
11. getPriority():获取线程的优先级
12. setPriority(int p):设置线程的优先级
.
.
线程的优先级
1.Thread类中给的三种优先级(设置时也可以自定义数值:1~10)
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5 --> 默认优先级
2.如何获取和设置当前线程的优先级:
getPriority():获取线程的优先级
setPriority(int p):设置线程的优先级
总结:高优先级的线程会抢占低优先级线程cpu的执行权。
但只是从概率上讲,高优先级的线程被执行的概率高一点而已,
并不意味着只有高优先级的线程执行完后,低优先级线程才执行。
.
.
线程分类:一种是守护线程
,一种是用户线程
。
.
.
.
线程的生命周期
.
.
.
线程的同步
线程安全问题:
1. 问题:买票过程中出现了重票、错票(0、-1) --> 出现了线程安全问题
2. 问题的原因:当某个线程执行操作车票的方法过程中,只执行了一部分尚未完成时,其他线程参与进来执行。导致共享数据的错误。
3. 如何解决:当一个 线程A 在操作共享数据的时候,其他线程不能参与进来。直到 线程A 操作完成共享数据时,
其他线程才可以开始操作共享数据。这种情况即使 线程A 出现了阻塞,也不能被改变!
.
.
同步机制
方式一:同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
说明:
① 需要被同步的代码 --> 操作共享数据的代码。 --> 不能包含代码多了(效率低、容易出错...),但一定包完共享数据!
② 共享数据 --> 多个线程共同操作的变量。
③ 同步监视器 --> 俗称:锁。
④ 锁 --> 任何一个类的对象都可以充当 或 当前类的本身(当前类.class)。 --> 当前类的本身只会加载一次!不像对象那样new几个有几个
要求:多个线程必须要共用同一把锁。!!!!!!!!!!!!!!!!
锁---说明:
在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
在继承Thread类创建多线程的方式中,考虑使用当前类充当同步监视器(类.class),慎用this充当同步监视器。
方式二:同步方法
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步。
private synchronized void show(){//同步监视器:this
//操作共享数据的代码
}
private static synchronized void show(){//同步监视器:当前类名.class
//操作共享数据的代码
}
① 同步方法仍然涉及到同步监视器,只是不需要我们显示的声明。
② 非静态的同步方法,同步监视器(锁)是:this
静态的同步方法,同步监视器(锁)是:当前类本身(当前类.class)
方式三:Lock锁 ---> JDK5.0新增
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。
ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,
在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。
格式:
1.实例化ReentrantLock类
private ReentrantLock lock = new ReenTrantLock(); //实现类方式创建多线程
private staic ReentrantLock lock = new ReenTrantLock(); //继承Thread类方式创建多线程
try{
//2.调用锁定方法
lock.lock();
//操作共享数据的代码
} finally{
//3.调用解锁方法
lock.unlock();
}
注意:
① 使用继承Thread方式创建的多线程,实例化ReenTrantLock类的对象要修饰为 static的 不然线程使用的就不是同一个锁了。
② 一定要用try...finally包起来,因为一定要执行 unlock() 来结束同步。
synchronized 与 Lock的异同?
相同:二者都可以解决线程安全问题
不同:
synchronized机制在执行完相应的同步代码后,自动释放同步监视器(锁)
Lock需要手动的启动同步 --> lock(),同时结束同步也需要手动的实现 --> unlock()
Lock只有代码块锁,synchronized有代码块锁和方法锁
synchronized 与 Lock 优先使用顺序:
顺序:Lock --> 同步代码块(已经进入了方法体,分配了相应资源)--> 同步方法(在方法体之外)。
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
同步方式的优劣
同步的方式,解决了线程的安全问题。--- 好处
操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。 --- 局限性
.
.
死锁问题
1. 死锁的理解:不同的线程分别占用对方需要的锁,都在等待对方放弃自己需要的锁,就形成了线程的死锁。
2.说明:
① 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。
② 我们使用同步时,要避免出现死锁
3.解决方法
① 专门的算法、原则
② 尽量减少同步资源的定义
③ 尽量避免嵌套同步
.
.
线程通信
涉及到的三个方法:
wait():一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器(锁)。
notify():一旦执行此方法,就会唤醒被 wait() 的一个线程。如果有多个线程被 wait(),就唤醒优先级高的(优先级一样就随机唤醒一个)。
notifyAll():一旦执行此方法,就会唤醒被 wait() 的所有线程。
说明:
1. wait()、notify()、notifyAll()三个方法必须使用在同步代码块或同步方法中。Lock里都不行(Lock有自己的方法实现通信)。
2. wait()、notify()、notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器(锁)!!!!!!!!!!!!!!!!!!,
否则,会出现IllegalMonitorStateException异常。
3. wait()、notify()、notifyAll()三个方法是定义在java.lang.Object类中。
面试题:sleep() 和 wait()的异同?
相同点:一旦执行方法,都可以使当前线程进入阻塞状态。
不同点:
① 两个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()
② 调用的要求不同:sleep()可以在任何需要场景下调用。wait()必须使用在同步代码块或同步方法中。
③ 如果两个方法都使用在同步代码块或同步方法中:sleep()不会释放锁,wait()会释放锁。