java语法规则
字面量和常量
程序中固定不变化的值。定义了就不能再去修改,比如:整数常量1,2,3,小数常量3.14,布尔常量false,true等。.
字面量,就表示直接给出的一个值(可以是整数,小数,true,false等等),直接量.
常量分类
整数常量
小数常量
布尔型常量
字符常量
字符串常量
null常量
示例
整数常量的表现形式
二制制 八进制 十进制 十六进制
变量
概念:其 值可以发生改变的量, 定义完毕后可以指代某一事物
定义:数据类型 变量名 = 初始化值;
特点
1.占据着内存中的某一块存储区域;
2.该区域有自己的名称(变量名)和类型(数据类型);
3.可以被重复使用;
4.该区域的数据可以在同一类型范围内不断变化;
标识符
什么是标识符
在写代码的时候为了增强代码的阅读性会自定义很多名字,比如:类名,方法名,变量名等
编程的里我们把这种为了增强程序阅读性而自定义的名称,称为标识符
命名规则
1.由字母、数字、下划线、$组成,但不能以数字开头(注:此处的字母可以是中文等)
2.区分大小写
3.不得使用java中的关键字和保留字
4.不用Java中内置的类名作为自己的类名
示例
关键字
什么是关键字
在编程语言中有一些事先定义的,有着特殊含义和用途的单词
什么是保留字
和关键字一样都是编程语言事先定义好的,只是说现在暂时没有特殊的用途
但说不定以后某天会突然被赋予意义和被使用到,因此被保留下来的单词,goto和const就是Java的保留字
有哪些关键字
表达式
定义
是由数字、运算符、数字分组符号(括号)、常量、变量等以能求得结果的有意义排列的组合
用运算符号连接的变量/常量可称为表达式
注释
注释的作用
注释信息是给程序员看的
编译器(javac)在编译的时候会忽略掉源文件中的注释信息
注释分类
- 单行注释
//注释信息
从//开始到本行结束的所有字符会被编译器忽略; - 多行注释;
/* 注释信息 /
/ 注释信息 */之间的所有字符会被编译器忽略 - 文档注释;
/ ** 注释信息 * /
/** 注释信息 */和多行注释一样,除此之外还可以专门生成文档信息API
示例
数据类型与语句结构
数据类型转换
数据类型概念
Java是强类型语言
对于每一种数据,都定义了明确的数据类型.
不同的数据类型,在内存当中分配不同的大空间
数据类型分类
- 基本数据类型
数值型:
- 整数
1. byte
2. short
3. int
4. long- 小数
1. float
2. double
字符型:char
布尔型:boolean
- 引用数据类型
类
接口
数组
数据类型占用空间大小
常用数据类型
-
boolean类型
通常用于逻辑运算和程序流程控制(条件选择/循环)
该类型的值只能是true 或 false,表示真或假
不可以使用0或非0的整数来代替false和true -
整数类型
byte、short、int、long
相同点都是存放整数类型的数据,不同点开辟的空间大小不一样,
Java语言的整型常量默认是int型,
声明long型变量后加上‘l’或‘L
因小写的l容易和数字1相混淆,建议使用大写L示例
byte b = 30; //8位
//b = 128;
short c = 32767; // 16位
int d = 324; //32位
long e = 68978L; //64位
//默认是int类型
System.out.println(100);
- 小数类型
float、double
float表示单精度类型
double表示双精度类型
浮点类型的字面量默认是double类型.若要声明一个常量为float型,则需在常量后加上f 或 F,double常量后面的D或d可省略
示例
//小数(float double)
double money = 100.5;
//相同点都是存放小数类型的数据,不同点开辟的空间大小不一样,
//默认情况下,小数是属于double
System.out.println(80.5);
//float money2 = 80.5;此行错误
float money2 = 80.5f;
- 字符类型
什么是编码
计算机只能表示0和1两个数,于是人们做规定使用一个数字去表示一个特定的字符
比如a使用97表示
char概述
char 字符,字母和符号 char类型:表示16位的无符号整数或者Unicode字符
Unicode收集了世界上所有语言文字中的符号,是一种跨平台的编码方式,Java的字符占两个字节,可以表示一个汉字
char常量有3种表示形式
直接使用单个字符来指定字符常量,格式如’◆’、‘A’,‘7’
直接作为十进制整数数值使用,但是数据范围在[0,65535],格式如97,但是打印出来的值依然是ASCII码表对于的符号,如97打印出来是字符
和2一样,只不过表示的是16进制的数值,格式如’\uX’,X表示16进制整数;如:97的16进制是61
ASSCII表
示例
/**
*字符本质是16位无符号小数
*使用一个数字去表示一个特定的字符
*字符类型
*/
System.out.println('a');
//字符型变量
char c = 'a';
c = 'b';
System.out.println(c);
//编码 0 1 字符类型
char c1 = 66; //ASSIC
System.out.println(c1);
//直接输出的是int类型
System.out.println(65);
基本数据类型转换
8个基本数据类型占用空间大小
8个基本数据类型
------------------------------------------
整数 byte->8 short->16 int->32 long->64
小数 float->32 double->64
字符 char->16
布尔 boolean -> 8
数据类型转换概念
把一种类型赋值给另外一种类型。
一种数据类型变换为另外一种数据类型
数据类型的转换
在运算过程当中, 如果不同的数据类型进行运算,可能运行的结果发生错误 把一种数据类型赋值给另一种数据类型
所以在运行之前,把类型进行统一.
数据类型转换的原则
boolean不属于数值类型,不参与转换.
系统可以完成自动类型转型
不能(直接)把一个大的数据类型直接赋值给小的数据类型
自动类型转换
当把小数据范围类型的数值或变量赋给另一个大数据范围类型变量
强制类型转换
当把大范围类型的数值或变量赋给另一个小范围类型变量时,此时系统不能自动完成转换,需要加上强制转换符
但这样的操作可能造成数据精度的降低或溢出,所以使用时要格外注意。
示例
自动类型提升
当一个算术表达式中包含多个基本数据类型(boolean除外)的值时
整个算术表达式的数据类型将在数据运算时出现类型自动提升
所有的byte、short、char类型被自动提升到int类型 整个表达式的最终结果类型被提升到表达式中类型最高的类型
运算符
算术运算符
用来处理四则运算的符号
加号
在操作数值、字符、字符串时其结果是不同的,当两个字符相加得到的是ASCII码表值
当两个字符串相加时表示将两个字符串连接在一起,从而组成新的字符串
除号
整数在使用除号操作时,得到的结果仍为整数(小数部分忽略) 当整数除以0的时候,会引发算术异常
取模(求余数)
模数的符号忽略不计,结果的正负取决于被模数.
自增++与自–
++: 表示当前操作变量自己累加1.
–: 表示当前操作变量自己减去1.
前置(++result): 表示对result加1之后的结果进行运算
后置(result++): 表示对result变量加1之前的值(原始值)进行运算。
赋值运算符
赋值运算符
就是将符号右边的值,赋给左边的变量。
比较运算符
概述
用于比较两个变量或常量之间的关系,比较运算符的结果是boolean类型 其操作格式为:boolean result = 表达式A
比较运算符 表达式B; 比较运算 == != > < >= <=
比较运算符的结果是一个布尔类型boolean
比较运算符
逻辑运算符
是用来连接两个布尔类型结果的运算符,运算结果都是布尔值 true 或者 false
逻辑运算符
- 所有的逻辑运算符结果都为布尔类型
- 与(&)
- 或(|)
- 非(!)
- 异或(^)
- 短路与(&&)
- 短路或(||)
示例
与(&)
或(|)
非(!)
异或(^)
短路与(&&)
短路或(||)
三元运算符
表达式
/**
* 表达式:执行指定的运算,
* 能够得出某一个特定的值。我们称这个式子为表达式
* 得出的值为数值开的式, 是算术表达式
* 如果得出的值是boolean 称它是逻辑表达式
*/
int a = 2 + 5; // 算术表达式 返回值是一个数值类型
boolean t = a > 5; //逻辑表达式 返回值是boolean
三元运算符格式
数据类型 变量名 = 布尔类型表达式?结果1:结果2
三元运算符计算方式
布尔类型表达式结果是true,三元运算符整体结果为结果1,赋值给变量
布尔类型表达式结果是false,三元运算符整体结果为结果2,赋值给变量
位运算符
位运算
Java 提供了 4 种位运算符
- 位与运算符(bitwise and operator):&
- 位或运算符(bitwise inclusive or operator):|
- 位异或运算符(bitwise exclusive or operator):^
- 位取反运算符(bitwise invert operator):~
这些运算符是在二进制补码上进行操作
示例
public static void main(String[] args) {
byte a = 15;
byte b = -15;
System.out.println(a & b);
System.out.println(a | b);
System.out.println(a ^ b);
System.out.println(~a);
System.out.println(~b);
}
一个字节数占 8 位,将 a,b 转换为二进制
a = 0000 1111
b = 1111 0001
位与运算符:仅当两个操作数同一下标的值均为 1 时,结果才为 1
a & b = 0000 1111 & 1111 0001 = 0000 0001(补) = 0000 0001(原) = 1
位或运算符:只要两个操作数同一下标的值有一个为 1 时,结果就为 1
a | b = 0000 1111 & 1111 0001 = 1111 1111(补) = 1000 0001(原) = -1
位异或运算符:只有两个操作数同意下标的值不相等时,结果才为 1
a ^ b = 0000 1111 ^ 1111 0001 = 1111 1110(补) = 1000 0010(原) = -2
位取反运算符:按位取反每一位
~a = ~0000 1111 = 1111 0000(补) = 1001 0000(原) = -16
~b = ~1111 0001 = 0000 1110(补) = 0000 1110(原) = 14
移位运算
Java 提供了 3 种移位运算符
左移运算符(left shift operator):<<
- 规则
左移几位,就在最低位补几个0 - 示例
3<<2 3乘2的2次方法 = 12
0000001100
3<< 3 3乘2的3次方法 = 24
00000011000 - 结论
左移几位,就是该数乘以2的几次方法
右移运算符(right shift operator):>>
- 规则
对于高位出现的空位,原来高位是什么,就拿什么来补 - 示例
6>>1
00000110
0000 0011
3
6>>2
00000110
0000 0001
1 - 结论
右移几位,就是该数除以2的几次幂
运算符优先级
顺序结构
顺序执行,根据编写的顺序,从上到下运行
示例
if
结构
执行流程
首先判断关系表达式看其结果是true还是false
如果是true就执行语句体
如果是false就不执行语句体
示例
* if(逻辑表达式){
* 要执行的语句
* }
* 1.当逻辑表达式的值 为true才去执行里面的语句
* 2.如果{}当中只有一条语句,那么{}可以省略不写的
// 如果{}当中只有一条语句,那么{}可以省略不写的
if(true)
System.out.println("我是语句");
int age = 18;
if(age >= 18) {
System.out.println("恭喜你成年啦,想干嘛就去干嘛吧");
}
if…else
格式
执行流程
首先判断关系表达式看其结果是true还是false
如果是true就执行语句体1
如果是false就执行语句体2
示例
/**
* if(逻辑表达式){
* 要执行的语句1 只有当表达式的值为true才会执行此处的语句
* } else {
* 要执行的语句2 如果逻辑表达式不为true就会执行此处的语句
* }
* 两者必须得要做一个选择。
*/
boolean isMan = false;
if(isMan) {
System.out.println("去男厕所");
}else {
System.out.println("去女厕所");
}
if…else if…else
结构
执行流程
首先判断关系表达式1看其结果是true还是false
如果是true就执行语句体1
如果是false就继续判断关系表达式2看其结果是true还是false
如果是true就执行语句体2
如果是false就继续判断关系表达式…看其结果是true还是false …
如果没有任何关系表达式为true,就执行语句体n+1
示例
* if(逻辑表达式){
* 要执行的语句1 只有当表达式的值为true才会执行此处的语句
* }else if(逻辑表达式){
* 要执行的语句1
* } else {
* 要执行的语句2 如果逻辑表达式不为true就会执行此处的语句
* }
* 多次的选择
* 如果已经满足条件了,后面的语句还会不会执行?
* 如果已经满足条件了,那么后面的语句就不会再去帮你执行。
* /
int a = 1;
if(a == 1) {
System.out.println("今天是星期1");
} else if(a == 1/0){ //验证是否执行了此语句,结果是没有 执行该 语句
System.out.println("今天是星期2");
} else if(a == 3){
System.out.println("今天是星期3");
}else if(a == 4){
System.out.println("今天是星期4");
}else if(a == 5){
System.out.println("今天是星期5");
}else if(a == 6){
System.out.println("今天是星期6");
}else if(a == 7){
System.out.println("今天是星期日");
}else {
System.out.println("你想要星期几啊");
}
switch
格式
执行流程
首先计算出表达式的值
其次,和case依次比较,一旦有对应的值,就会执行相应的语句,在执行的过程中,遇到break就会结束
最后,如果所有的case都和表达式的值不匹配,就会执行default语句体部分,然后程序结束掉。
case的穿透
在switch语句中,如果case的后面不写break,将出现穿透现象, 也就是不会在判断下一个case的值,直接向后运
行,直到遇到break,或者整体switch结束
示例
/* switch(整型表达式){
* case 值A: 执行语句; break;
* case 值B: 执行语句; break;
* case 值C: 执行语句; break;
* ....
* default:上面没有一个满足条件 ,就会执行此语句
* }
*/
int a = 8;
switch(a) {
case 1 : System.out.println("星期1");break;
case 2 : System.out.println("星期2");break;
case 3 : System.out.println("星期3");break;
case 4 : System.out.println("星期4");break;
case 5 : System.out.println("星期5"); break;
case 6 : System.out.println("星期6");break;
case 7 : System.out.println("星期日");break;
default:System.out.println("你想要星期几啊");
}
循环结构
概述
循环语句可以在满足循环条件的情况下 反复执行某一段代码,这段被重复执行的代码被称为循环体语句 当反复
执行这个循环体时,需要在合适的时候把循环判断条件修改为false, 从而结束循环,否则循环将一直执行下去,形 成死循环。
while
结构
执行过程
先判断表达式,若为true就执行循环体,否则,跳过循环体。
示例
int count = 0;
while(count < 2) {
System.out.println("你很帅");
count++;
}
System.out.println("ending");
//2.打印1 到 100所有的数字
int num = 1;
while(num <= 100) {
System.out.println(num);
num++;
}
do while
结构
执行流程
先执行一次循环体,再判断表达式,若为true就执行循环体,否则,跳过循环体。 也就是说do
while是先执行后判断,即使判断条件为false,该循环至少会执行一次
示例
/**
* do{
* 循环体
* }while(逻辑表达式)
*
* 不管逻辑表达式为不为真,上来先执行一次循环体。
*/
int count = 0;
do {
System.out.println("你很帅");
count++;
}while( count < 5);
for
结构
执行流程
初始化语句:表示对循环进行初始化,只在循环开始时执行一次,定义一个变量,并赋值。
boolean表达式:表达式为false时,循环终止,为true,才执行循环体。
循环后操作语句:循环每次迭代之后会调用该语句,一般的该语句都是递增或递减操作。
示例
/ * for(初始化的语句; 逻辑表达式 ; 循环之后的操作语句){
* 循环体
* }
* 1.初始化的语句 (只会执行一次)
* 2.会执行逻辑表达式
* true: 执行循环体
* false: 跳过循环
* 3. 执行循环体
* 4. 执行循环体之后的操作语句
* 5. 进入到第二步继续判断
*/
for(
int count = 0; //1.初始化语句
count < 5; //2.逻辑表达式
count++ //3.循环之后的操作语句
)
{
System.out.println("你很帅"); //4. 循环体
}
for(int num = 1; num <= 100; num++) {
System.out.println(num);
}
死循环
也就是循环中的条件永远为true,死循环的是永不结束的循
while
do while
for
嵌套循环
一个循环的循环体是另一个循环。比如for循环里面还有一个for循环
控制循环结构
break语句
终止当前所在的循环
break之后的语句执行不了,所以不能编写.
示例
for(int i = 1; i<= 100; i++) {
System.out.println(i);
if(i == 10) {
break;
}
}
//打印1到100之间前5个偶数
for(int i = 1; i<=100; i++) {
if(i % 2 == 0) {
System.out.println(i);
count++;
}
if(count == 5) {
break;
}
}
System.out.println("end................");
continue语句
跳过当前的循环,进入下一次循环操作
示例
return
表示结束循环所在的方法,方法都结束了,循环结构自然也就结束了
总结
- break:终止循环,跳出循环。继续执行后续的代码
- continue:跳出本次循环,后续的循环继续执行,循环执行完毕,后续的代码继续执行。
- return:循环终止,后续的代码也不执行了。
控制外层循环
标签就是给某个循环起的别名,不过该别名得满足标识符的规范。
若要控制外层循环,就在break或continue后面跟上循环的标签名称
outter : for(int count= 1; count <= 5; count++) {
inner : for(int i = 0; i < count;i++) {
if(count == 3) {
break outter;
}
System.out.print("*");
}
System.out.println();
}
方法与数组
方法概述与定义
方法介绍
就是一个小功能。把一段代码存起来,以后可以重复使用
示例
static void printB() {
System.out.println("b");
System.out.println("b");
System.out.println("b");
System.out.println("b");
}
方法注意点
- 注意点:
- 1.方法必须声明到类(class)当中
- 2.创建的方法要定义在其它方法之外(跟其它的方法是兄弟关系)
- 3.定义方法不分先后顺序
- 4.如果一个方法前面有static,那么在该方法当中调用其它的方法前面也应该有static
- 5.一个类当中可以写多个方法
- 6.两个方法的名称不能相同
- 7.方法必须得要先定义,再执行。
参数
方法的完整形式:
/
*
* [修饰符] 返回值类型 方法名([形式参数...]){
* 方法体(要重复使用的代码)
* [return 值]
* }
*
* void 返回值为空 什么都不返回 , 如果有返回值 ,里面必须得要有return
* /
方法返回值
给定两个字符串 把两个字符串拼接到一起 并返回
/**
* 求两个数的和
* @param a 第一个数
* @param b 第二个数
* 在此处声明的变量,只在方法当中有效。
* 出了方法,就没有效果,外界的名称也不会影响里面
* return后面是什么 类型,那么 定义返回值时,就要定义什么 类型。
* 定义了有返回值 ,接不接收返回值都可以
*/
static int sum(int a,int b) {
int res = a + b;
return res;
}
驼峰命名法
第一个字母小写,后面的每一个单词首字母大写
示例
static String stringConcat(String str1,String str2) {
//System.out.println(str1 + str2);
String res = str1 + str2;
return res;
}
方法重载
概念
在同一个类中,允许方法名称相同,但是方法的参数必须不同
方法 的重载解决了,功能相同,参数不同,就不需要定义多个方法名称了
方法的重载的规则
方法名称必须相同。
参数列表必须不同。
方法的返回类型可以相同也可以不相同。
仅仅返回类型不同不足以称为方法的重载。
示例
数组
概念
它是把相同的数据类型有序的组成在一起, 我们称这样的集合为数组。
相同的数组类型
有序的组成在一起
规则
/**
* 数组:帮我们存放相同类型的东西的。
* 1.存放 的东西必须得是相同 的数据类型
* 2.存放的东西都是有序的。可以通过角标来获取存的内容
*/
数组初始化
静态初始化
在创建数组时,就已经确定了有多少个元素,
并且把元素都放到了数组当中
示例
//int[] ages = new int[] {21,22,23,21,25};
String[] names = new String[] {"zs","ls","wc"};
//先定义,再赋值
int a;
//System.out.println(a); 直接使用是错误的
a=10;
int[] ages;
//System.out.println(ages); 直接使用是错误的
//如果不初始化,数组是不能使用的。
ages = new int[] {21,22,23,21,25};
String name = names[1];
System.out.println(name);
动态初始化
一开始不知道要存哪些数数据。所以先定义一个指定的空间大小
示例
String[] allPerson = new String[100];
allPerson[0] = "zs";
allPerson[1] = "ls";
//ArrayIndexOutOfBoundsException:数组越界
System.out.println(allPerson[101]);
数组长度
获取数组元素的个数: 数组名称.length
示例
取出数组当中每一个元素
示例:
排序算法
冒泡排序
演示地址
http://www.jsons.cn/sort/
升序为例
第1步
从头开始比较每一对象元素,如果第1 个比第2个大就交换他们的位置
执行完一轮后, 最大的元素 在最后.
第2 步
当执行完一轮后, 最大的元素,在后面
还要继续执行和第1轮相同的操作,索引的最大值要-1
int arr[] ={7,4,3,6,9};
for (int end = arr.length; end > 0; end--){
for (int begin = 1; begin < end; begin++){
if (arr[begin] < arr[begin -1]){ //左边的数比右边的大
//交换两个数的位置
int temp = arr[begin];
arr[begin] = arr[begin -1];
arr[begin-1] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
选择排序
从序列中找出最小的元素,然后与第一个元素进行交换
执行完一轮后, 第一个元素就是最小的
每交换一次最小的值,位置向后移动一个
for (int start = 0; start < arr.length; start++) {
//定义一个变量 记录最小值的角标
int minIndex = start;
for (int next = start + 1;next <= arr.length - 1; next++)
{
if ( arr[next] < arr[minIndex]){
minIndex = next;
}
}
//拿最小的值 , 与第一个元素进行交换
int temp = arr[start];
arr[start] = arr[minIndex];
arr[minIndex] = temp;
}
二分查找算法
什么是二分查找
二分查找(binary search)又叫折半查找,它是一种在有序数组中查找某一特定元素的搜索算法
二分查找必要条件
必须为顺序存储结构
必须按关键字大小有序排列
原理
将有序数组分为三个部分
- 中间值前(中间值数之前的一组数据)
- 中间值
- 中间值后(中间值之后的一组数据)
将要查找的数与中间值的数相比较
- 等于则退出查找
- 小于则在中间值前进行比较
- 大于在在中间值后进行比较,依次循环操作,直至查找到对应的值为止;
当要查找数据结构为偶数时,中间值mid应向下取整处理;
示例图
代码实现
public static int binarySearch(int[] nums, int target) {
int low = 0;
int high = nums.length - 1;
if (low > high || target > nums[high] || target < nums[low]){
return -1;
}
while(low <= high) {
//取中间值
int mid = (high + low) / 2;
//等于则退出查找
if(nums[mid] == target)
return mid;
else if (nums[mid] < target)
//小于则在中间值前进行比较
low = mid + 1;
else if (nums[mid] > target)
//大于在在中间值后进行比较
high = mid - 1;
}
return -1;
}
二维数组
概念
在数组当中存放 的每一个元素又是一个数组
格式:
int[] classAge1 = {18,19,20,17};
int[] classAge2 = {18,19,20};
int[] classAge3 = {18,19};
//整个年级的年龄
//int[][] allClassAges = new int[][] {classAge1,classAge2,classAge3};
int[][] allClassAges = new int[][] {
{18,19,20,17},
{19,19,20},
{30,19}
};
二维数组遍历
for(int i = 0; i < allClassAges.length; i++) {
int[] array = allClassAges[i];
for(int j = 0; j < array.length; j++) {
System.out.println(array[j]);
}
}
快速遍历
普通遍历
int[] ages = {10,20,30};
//遍历数组
for(int i = 0; i < ages.length; i++) {
System.out.println(i);
System.out.println(ages[i]);
}
快速遍历
//遍历数组foreach
//for (数组类型 遍历出来的元素 : 数组的名称)
for (int i : ages) { //是一个语法糖
System.out.println(i);
}
String[] names = {"zs","ls","xq","123"};
for (String name : names) {
System.out.println(name);
}
可变参数
数组当作参数传递
static void sum(int[]args) { //把数组当作参数进行传递
int res = 0;
for(int i = 0; i < args.length; i++) {
res = res + args[i];
}
System.out.println(res);
可变参数
static void sum(int...args) { //语法糖
int res = 0;
for(int i = 0; i < args.length; i++) {
res = res + args[i];
}
System.out.println(res);
}
public static void main(String[] args) {
int[] params = {1,2,3,4};
sum(params);
sum(1,2,3,4);
}
变量作用域
什么是作用域
生效(能使用的范围)的范围
变量作用域
生效范围是:在创建变量的大括号当中生效。超过了大括号就不生效(不能再使用了)
在方法内部定义的变量,跟外界没有任何关系
示例
test();
int a = 10;
System.out.println(a);
{ //代码块
int b = 20;
System.out.println(b);
}//b过完大括号就没有了
int b = 30;
面向对象
面向过程
概念
是站在过程的角度思考问题,强调的就是功能行为,功能的执行过程,
即先干啥,后干啥。
而每一个功能我们都使用函数(类似于方法)把这些步骤一步一步实现,使用的时候依次调用函数就可以了
按顺序一个一个来去做
面向过程的设计
最小的程序单元是函数,每个函数负责完成某一个功能,用以接受输入数据,函数对输入数据进行处理,然后输出结果数据.
整个软件系统由一个个的函数组成,其中作为程序入口的函数称之为主函数,
主函数依次调用其他函数,普通函数之间可以相互调用,从而实现整个系统功能.
面向过程的缺陷
面向过程的设计,是采用置顶而下的设计方式, 在设计阶段就需要考虑每一个模块应该分解成哪些子模块,每一个子模块有细分为更小的子模块,
如此类推,直到将模块细化为一个个函数.
基本理论
什么是对象?
万物皆对象
对象是具体物体
拥有属性
拥有行为
把很多零散的东西, 封装成为一个整体
面向过程&面向对象
都是一种解决问题的思路(思想)
面向过程
在解决问题的时候,关注的是解决问题的每一个的过程(步骤)
面向对象
在解决问题的时候,关注的是解决问题所需要的对象
类
什么是类?
某一个具体对象特征的抽象
类的作用?
根据抽象的类,生产具体的对象
对象和类的关系
对象
抽象
类
实例化
对象
JVM内存分析
JVM将内存主要划分为:方法区、栈、本地方法栈、堆、程序计数器。
- 虚拟机栈
执行引擎每调用一个函数时,就为这个函数创建一个栈帧,并加入虚拟机栈。
换个角度理解,每个函数从调用到执行结束,其实是对应一个栈帧的入栈和出栈
声明的变量都是存到栈当中的 - 堆
被所有线程共享的一块区域,在虚拟机启动时创建,所有的对象实例及数组都在堆上分配
使用new关键字,表示在堆中开辟一块新的存储空间 - 方法区存
又叫静态区,存放所有的class字节码和static变量;方法区中包含的都是在程序中永远的唯一的元素 - 程序计数器
每个线程得有个计数器记录当前执行到那个指令。可以把它看成是当前线程所执行的字节码的行号指示器。 - 本地方法区
本地方法栈与虚拟机栈所发挥的作用很相似,他们的区别在于虚拟机栈为执行Java代码方法服务,而本地方法栈是为Native方法服务
- GC
数据类型
基本数据类型
只有一块存储空间, 在栈中,存放的是具体的值
引入数据类型地址传递
引用数据类型有两块存储空间
一个在栈(Stack)中,一个在堆(heap)中。 栈中存放的是堆中的地址
栈中的存取速度要快于存储在堆中的对应包装类的实例对象。
基本数据类型与引用数组类型总结
匿名对象与构造器
匿名对象
匿名对象就是没有名字的对象
匿名对象只能使用一次
示例
Student stu = new Student();//这个stu就是对象,名字就是stu
new Student();//这个也是一个对象,但是没有名字
构造器
构造器概述
方法名称与类名相同这种特殊方法我们称之为构造器
某一个类,至少存在一个构造器.
构造器作用
创建对象,凡是必须和 new 一起使用.
完成对象的初始化操作.
构造器特点
1.构造器的名称和当前所在类的名称相同.
2.禁止定义返回类型,千万不要使用void作为返回类型. void Person(){}这是普通方法.
3.在构造器中,不需要使用return语句.
构造器其实是有返回值,它返回的是当前创建对象的地址
默认构造器的特点
1.符合构造器特点.
2.无参数的.
3.无方法体.
4.如果类A没有使用public修饰,则编译器创建的构造器也没有public修饰.使用了public修饰,则编译器创建的构造器也使用public修饰.
示例
自定义构造器
我们可以自己来去定义构造器,自定义的构造器也要符合构造器的特点
1.我们自己写的构造器,就称为自定义构造器
2.如果我们自己定义了构造器,则编译器不再创建默认构造器
3.一个类至少存在一个构造器。
4.创建对象其实是在调用构造器。
示例
public class User {
String name;
User(String userName){
System.out.println("---执行了自己的构造器-");
//在构造器当中,对字段做了初始化
name = userName;
}
public static void main(String[] args) {
User user = new User("fmjava");
System.out.println(user.name);
}
}
构造器重载
方法重载:方法名相同 ,参数不一样。
构造器重载:构造器的名相同 ,参数不一样。
参数不一样,在创建对象时,根据参数不同,调用不同的构造器
示例
static关键字
static作用
通过Static修饰符就能解决这个问题,它修饰的成员就不属于对象,它属于类本身。
它可以修饰字段,方法,内部类。
作用:确定修饰的内容是属于类还是属于对象。
static特点
1.Static修饰的内容,附着类的加载而加载
2.优先于对象的存在
3.Static修饰的成员被该类型的所有对象共享
4.可以直接使用当前类的类名访问static成员
总结
成员变量与局部变量
变量: 数据类型 变量名 = 值;
成员变量
直接定义在类中,方法的外面,也称全局变量或字段,不要叫属性。
1.类成员变量: 使用static修饰的字段.
2.实例成员变量: 没有使用static修饰的字段.
3.在方法不能定义static变量,static属于类, 方法属于对象。
局部变量
变量除了成员变量,其他都是局部变量.
- 方法内部的变量.
- 方法的参数
- 代码块中的变量
成员变量与局部变量区别
变量的初始值
成员变量: 默认是有初始值的
局部变量: 没有初始值,所以必须先初始化才能使用
变作的使用域
成员变量: 在整个类中都有效
局部变量:只在它定义的大括号内生效
成员变量,可以先使用后定义,局部变量必须先定义而后才能使用.
作用域及生命周期
变量的生命周期指的是一个变量被创建并分配内存空间开始,到该变量被销毁并清除其所占内存空间的过程。
作用域不同,生命周期就不一样
变量使用注意点
1.局部变量定义后,必须显示初始化后才能使用,因为系统不会为局部变量执行初始化操作。 这就意味着,定义局部变量后,系统并未为这个变量分配内存空间。直到程序为这个变量赋值时,系统才会为局部变量分配内存,并将初始值保存到该内存中。
2.局部变量不属于任何类或实例,因此它总是保存在其所在方法的栈内存中。
3.基本数据局部变量:直接把这个变量的值保存到该变量所对应 的内存中。引用数据局部变量:这个变量内存中存的是地址,通过该地址引用到该变量实际引用堆里的的对象。
4.栈内存中的变量无需系统垃圾回收,其往往随方法或代码块的运行结束而结束。
public class Person {
static int totalNums;
String name;
int age;
int times;
void eat(String food) {
int a = 10;
if(times >= 3) {
System.out.println("不能再吃了");
}else {
System.out.println("吃了"+ food);
times++;
}
}
Person(String n,int a){
name = n;
age = a;
Person.totalNums++;
}
public static void main(String[] args) {
Person per = new Person("zs",18);
System.out.println(Person.totalNums);
per.eat("米饭");
}
}
封装与继承
封装思想
什么是封装
把对象的状态和行为看成一个统一的整体 , 将字段和方法放到一个类中
信息隐藏: 把不需要让外界知道的信息隐藏起来
尽可能隐藏对象功能实现细节,向外暴露方法,保证外界安全访问功能;
封装的好处
1.保证数据的安全
2.提高组件的复用性
包
概述
在开发中,我们存在几百上千个Java文件,如果所有的Java文件都在一个目录中,管理起来也很痛苦.
此时,我们可以使用生活中的解决方案,此时在Java中,我们把这个特殊文件夹称之为包(package). 关键字:package ,专门用来给当前Java文件设置包名.
注意点
同一个包中的类名不能相同,不同的包中的类的名字是可以相同的,
当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
语法格式
package 包名.子包名.子子包
必须把该语句作为Java文件中,并且是第一行代码(所有代码之前)
包名定义
包名必须遵循标识符规范 全部小写
使用格式:Package域名倒写.模块名.组件名
Import语句
当A类和B类不在同一个包中,若A类需要使用到B类,此时就得让A类中去引入B类.
系统提前帮我们做了很多功能,他们都放到了包里面,我们只需要把它们从指定的包里面导入,就可以使用了
使用系统提供的功能,需要从系统的包里面导入类
访问修饰符
什么是访问修饰符
访问权限修饰符来规定在一个类里面能看到什么,能暴露什么
访问修饰符分类
- private:表示私有的, 表示类访问权限. 只能在本类中访问,离开本类之后,就不能直接访问.
- protected:表示子类访问权限,同包中的可以访问,不同包不能访问,继承也可以访问.
- public:表示全局的,可以公共访问权限, 使用了public修饰,则可以在当前项目中任何地方访问.
- 默认:表示包访问权限. 访问者的包必须和当前定义类的包相同才能访问.
作用域
属性与this关键字
属性
什么是属性?
属性和字段类似,也是一个成员变量
但是必须得要有getter/setter方法
有getter/setter方法才是属性,没有就是字段
一般属性都是私有的,公有的别人可以直接获取和设置值
this
this关键字作用
this就是代表当前正在使用的对象地址
this使用及注意点
1.解决成员变量和参数(局部变量)之间的二义性,必须使用;
2.同类中实例方法间互调(此时可以省略this,但是不建议省略)。
3.将this作为参数传递给另一个方法;
4.将this作为方法的返回值(链式方法编程);
5.构造器重载的互调,this([参数])必须写在构造方法第一行;
6.static不能和this一起使用;
当字节码被加载进JVM,static成员以及存在了.
但是此时对象还没有创建,没有对象,就没有this.
this构造器
继承思想
引出继承
代码问题
上面三个类,发现有共同的代码
如何来去解决代码重复的问题。
我们就可以使用继承来去解决上面问题
什么是继承
从已有类中,派生出新的类,新的类中吸收已有类当中的状态和行为。
把多个类中相同的属性和行为进行提取
派生出一个新的类
Java继承是使用已有类作为基础,建立新的类。
继承是一种从一般到特殊的关系,是一种“is a”的关系,
即子类是对父类的派生,是一种特殊的父类,
比如:狗是动物的一种特殊情况,狗属于动物。
卡车是车的一种特殊情况,卡车属于车…
如何表示继承
语法格式: 在定义子类的时候来表明自己需要拓展于哪一个父类.
public class 子类类名 extends 父类类名
{
编写自己特有的状态和行为
}
示例
继承作用
1.解决代码重复问题
2.表示出一个体系
继承编写规则
一般的,我们在开发工程中先编写多个自定义类,写完之后, 发现多个类之间存在共同的代码,此时可以抽去出一个父类.
子类与父类的继承关系
子类继承父类之后,可以拥有父类的某一些状态和行为。
子类复用了父类的功能或状态.
但并不是父类当中所有的内容,子类都可以直接使用。
子类可以使用父类当中的哪些成员
1.如果父类中的成员使用public修饰,子类继承.
2.如果父类中的成员使用protected修饰,子类也继承,即使父类和子类不在同一个包中.
3.如果父类和子类在同一个包中,此时子类可有继承父类中 缺省修饰符的成员.
4.如果父类中的成员使用private修饰,子类打死都继承不到.因为private只能在本类中访问.
5.父类的构造器,子类也不能继承,因为构造器必须和当前的类名相同.
方法的覆盖Override
什么是方法覆盖
子类拓展了父类,可以获得父类的部分方法和成员变量。
可是当父类的某个方法不适合于子类本身的特征时,可以进行覆盖 重新定义父类当中的方法
方法的覆盖原则
1.实例方法签名必须相同(方法名+方法参数)
2.子类方法的返回值类型是和父类方法的返回类型相同或者是其子类。
3.子类方法声明抛出的异常类型和父类方法声明抛出的异常类型相同或者是其子类
4.子类方法的访问权限比父类方法访问权 限更大或相等
判断方法是否为覆盖方法
判断是否是覆写方法使用@Override标签 若方法是覆写方法,在方法前或上贴上该标签, 编译通过,否则,编译出错。
只有方法存在覆盖的概念,字段没有覆盖.
什么时候进行方法的覆盖
当父类的某一个行为不符合子类具体的特征的时候,此时子类需要重新定义父类的方法,并重写方法体.
方法的重载和方法覆盖的区别?
方法重载Overload
作用
解决了同一个类中,相同功能的方法名相同的问题.使用参数不同
既然是相同的功能,那么方法的名字就应该相同.
规则
同类中,方法名相同,方法参数列表不同
方法重写/覆盖: Override
作用
解决子类继承父类之后,可能父类的某一个方法不满足子类的具体特征,此时需要重新在子类中定义该方法,并重写方法体.
规则
父类和子类的方法签名是相同的
super关键字
作用
当前对象的父类对象
在子类中的某一个方法中,去调用父类被覆盖的方法.
此时的解决方案:使用super关键字
Super关键字使用的场景
1.可以使用super解决子类隐藏了父类的字段情况., 该情况,我们一般不讨论,因为破坏封装.
2.在子类方法中,调用父类被覆盖的方法
3.在子类构造器中,调用父类构造器,此时必须使用super语句:super([实参]).
static不能和super以及this共存
子类初始化过程
在创建子类对象之前,会先创建父类对象.
调用子类构造器之前,在子类构造器中会先调用父类的构造器,
默认调用的是父类无参数构造器
隐藏
1满足继承的访问权限下,隐藏父类静态方法:若子类定义的静态方法的签名和超类中的静态方法签名相同,
那么此时就是隐藏父类方法。注意:仅仅是静态方法,子类存在和父类一模一样的静态方法.
2.满足继承的访问权限下,隐藏父类字段:若子类中定义的字段和超类中的字段名相同(不管类型),此时就是隐藏父类字段,此时只能通过 super 访问被隐藏的字段。
3.隐藏本类字段:若本类中某局部变量名和字段名相同,此时就是隐藏本类字段,此时只能通过this访问被隐藏的字段。
方法隐藏示例
UML
什么是UML
又称标准建模语言
可以用来表示软件开发过程当中类与类之间的关系
主要模型
- 功能模型
从用户的角度展示系统的功能,包括用例图。 - 对象模型
采用对象、属性、操作、关联等概念展示系统的结构和基础,包括类图、对象图、包图。 - 动态模型
展现系统的内部行为。 包括序列图、活动图、状态图。
类图的画法
示例
权限表示
1、public 公用的 :用+ 前缀表示 ,该属性对所有类可见
2、protected 受保护的:用 # 前缀表示,对该类的子孙可见
3、private 私有的:用- 前缀表示,只对该类本身可见
4、package 包的:用 ~ 前缀表示,只对同一包声明的其他类可见
5、静态成员: 用_表示
多态/代码块/FINAL关键字
多态
什么是多态
对象具有多种形态,对象可以存在不同的形式
父类指针指向子类对象
在方法调用时,通过父类进行调用,真正执行时, 调用的是子类方法,这种特征我们称之为多态
多态的特点
把子类对象赋给父类变量
在运行时期会表现出具体的子类特征
调用子类的方法.
示例
多态的作用
1.当把不同的子类对象都当作父类类型来看待,可以屏蔽不同子类对象之间的实现差异, 从而写出通用的代码达到通用编程,以适应需求的不断变化
多态方法调用问题
1.只有父类当中有方法
2.只有子类有,父类没有方法
3.子类和父类都有调用的方法
instanceof关键字
作用:判断一个对象是否是指定 的类
void feedAnimal(Animal anim) {
System.out.println(anim.getClass());
anim.eat();
/**
1.类的强制类型转换:把父类对象赋值给子类类型
2.instanceof : 判断一个对象是否是指定的类,
如果是 返回 true 不是, 就返回 false
*/
if(anim instanceof Dog) {
Dog dog = (Dog)anim;
dog.doWork();
}else if(anim instanceof Cat) {
Cat cat = (Cat)anim;
cat.watch();
}
}
代码块
概念
在类中或方法当中 使用{}括起来的一段代码 就称它是一个代码块
总结
分类
- 局部代码块
直接定义在方法内部 的代码块 在调用方法的时候执行。
示例
- 初始化代码块
直接在类当中定义代码块
初始化代码块在运行时,还是要把它放到构造方法当中
示例
- 静态代码块
在初始化代码块前面加上一个static
在什么时候执行:在加载字节码时就会自动调用
在主方法之前执行的。只执行一次。
示例
- 静态代码块
类加载问题
类的加载
当第一次创建该 类对象的时候,加载到内存当中
在加载时,会执行Static
字段初始化问题
静态字段初始化:是在静态代码块当中初始化
非静态的字段初始化:它是在构造器当中做的初始化
子类构造器默认会调用父类的构造器
final关键字
继承弊端
破坏了我们的封装 它可去访问父类当中 的实现 细节 可以覆盖父类当中 的方法
final作用
“最终,不可修改的” 保证数据的安全
可以修饰内容
- 字段
不能再去修改该 字段,定义常量 - 方法
子类就能再去覆盖该 方法 - 类
该类就不能再去被继承
注意点
- final 修饰字段时, 字段是没有初始值 ,必须得要自己手动设置初始值
* final 修饰变量,就代表是一个常量 命令规则 :所有的字母都大写MAX_VALUE
* final 可以在局部代码块当中使用
* 如果final修饰的是基本数据类型 代表值不能再去修改了
* 如果final修饰的是一个引用类型 地址不能再去修改
单例设计模式
设计模式
之前很多程序员经常无数次的尝试,总结出来一套最佳实践。
单例
一个类在内存当中只有一个对象。别人不能再去创建对象。
饿汉模式
1.必须得要在该 类中创建一个对象出来。
2.私有化自己 的构造器。防止外界通过构造器来创建新的对象。
3.给外界提供一个方法,能够 获取已经创建好的对象。
class ToolUtil{
int num = 1;
//1.必须得要在该 类中创建一个对象出来。
private static ToolUtil instance = new ToolUtil();
//2.私有化自己 的构造器。防止外界通过构造器来创建新的对象。
private ToolUtil() {}
//3.给外界提供一个方法,能够 获取已经创建好的对象。
static public ToolUtil getInstance() {
return instance;
}
public void show() {
this.num++;
System.out.println(num);
}
}
接口/内部类
抽象方法
引出
求圆 ,矩形,三角形,面积
示例
发现的问题
每一个图形计算面积的方式都不一样,所以每一个类都必须得要覆盖父类当中的getArea方法,来去实现求面积的方式
带来的问题
怎么样保证子类必须得要覆盖父类当中定义的方法。
父类不需要提供方法体
抽象方法
格式:在方法 前面添加了一个关键字abstract
抽象方法特点
1.抽象方法是没有方法体的
2.抽象方法必须得要定义在抽象类 或 接口当中
3.抽象方法不能是私有的private,final,static
示例
注意点
子类必须得要去覆盖此方法
在定义的时候不要方法体了。
抽象类
概念
abstract修饰的类 就成为了抽象类
抽象类必须得要有子类才行。(抽象类一般都当作父类来继承)
抽象类当中 的注意点
1.抽象类是不能直接创建对象的。
2.抽象类当中,可以有抽象方法 ,也可以有非抽象方法(普通方法:给子类调用的)
3.子类没有去覆盖抽象方法 ,把子类也变成抽象类
4.构造方法不能定义为私有化(抽象方法必须得要让子类继承之后,才能实现内部的方法体。子类继承的话,先去调用父类的构造方法)
5.抽象类不能使用final来去修饰
抽象类与普通的类有什么区别?
普通类你有的(方法 ,字段,构造器),抽象类都有
抽象不能创建对象。抽象类可以包含抽象方法,也可以包含非抽象方法
抽象类必须有子类才有意义,
接口
概念
- 物理接口
指的是两个硬件设备之间的连接方式。硬件接口既包括物理上的接口,比如我们所以说的USB接口
示例
- Java接口
在Java中,接口表示一种规范/约束/要求实现者必须遵循该规范:用来约束使用者应该怎么做.
规范和实现相分离的好处
主板上提供了USB插槽,只要一个遵循了USB规范的鼠标,就可以插入USB插槽,并与主板正常通信。至于这个鼠标是谁生产的,内部是如何实现的,主板都不需要关心(只要遵循了USB规范就可以插在主板上使用)。当鼠标坏了的时候,我主板不会坏。
Java接口定义
概念
java接口也是表示一种规范,使用抽象方法来去定义一组功能,
必须要实现者给提供方法的实现。
接口定义
interface 接口名称 {
}
接口当中的注意点
1.接口是没有构造器,接口是不能创建对象。
2.接口当中定义变量,都是全局的静态常量
3.接口当中 定义的方法 都是公共的抽象的方法
4.接口是可以继承,它是可多继承
interface 接口名称 extends 继承的接口名, 继承的接口名
接口实现
类名 extends 其它的类 (只能继承一个类) implements 其它的接口(接口可以实现多个)
接口与抽象类区别
- 相同点
* 1.都是被其它类实现
* 2.都不能实例化
* 3.都可以定义抽象方法 ,定义的抽象方法子类都必须得要覆盖。 - 不同点
* 1.接口是没有构造器,抽象类当中是有构造器。
* 2.抽象类可以包含普通方法和抽象方法,接口当中只能有抽象方法,不能有普通方法(带有方法体)
* 3.接口当中默认成员变量,要有初始值 都是静态的
* 4.方法 :接口当中默认方法 public abstract 方法 名; 抽象类 默认的权限
面向接口编程
概述
把实现类对象赋值给接口类型的变量 多态的好处:屏蔽了不同类之间实现差异,从而达到通用编程
接口多态
把实现类赋值给接口 运行时, 运行的仍是实现类
内部类
什么是内部类
定义在类当中 的一个类
内部类可以直接访问外部类当中 的成员
示例
为什么要有内部类
1.增强封装,把内部类隐藏在外部类当中,不允许其它类访问这个内部类
2.增加了代码一个维护性
内部类分类
实例内部类
直接定义在类当中的一个类,在类前面没有任何一个修饰符,不属于类的,不使用static修饰的内部类
示例
实例内部类的创建
想要使用内部类,必须得要先创建外部类
示例
实例内部类注意点
1.想要使用内部类,必须得要先创建外部类
2.在内部类当中可以访问外部类当中的成员
3.在内部类当中,不能有静态的成员
4.外部类是不能直接访问内部当中 的成员
总结
静态内部类
在内部类前面加上static,属于类的内部类
静态内部类的创建
静态内部类注意点
1.静态内部类是不需要创建外部对象
2.在静态内部类当中 ,是没有外部类引用
3. 静态内部类,是可以访问外部类的静态成员
4.访问静态内部类当中的静态成员
5.静态内部当中可以定义静态成员,也可以定义非静态成员
总结
静态内部类当中访问外部 的普通变量
匿名内部类
就是一个没有名字的局部内部类
只使用一次的时候,来去使用 匿名内部类
匿名内部类必须得要有父类才, 或者是实现了接口
new 父类的构造器 或 接口(){
内部写的代码(在new时候就会自动执行)
}
枚举
引出枚举
什么是枚举
枚举:比如:季节(春,夏,秋,冬)
星期:周一到周日
性别:男女
表示一个事件固定状态
枚举定义
[修饰符] enum 枚举的名称 {
常量1,常量2,常量3.。。
}
工厂设计模式
工厂设计模式好处
- 工厂模式是为了解耦
就是Class A 想调用 Class B ,那么A只是调用B的方法,而至于B的实例化,就交给工厂类。 - 工厂模式可以降低代码重复
如果创建对象B的过程都很复杂,需要一定的代码量,而且很多地方都要用到,那么就会有很多的重复代码。我们可以这些创建对象B的代码放到工厂里统一管理。既减少了重复代码,也方便以后对B的创建过程的修改维护。 - 因为工厂管理了对象的创建逻辑,使用者并不需要知道具体的创建过程,只管使用即可,减少了使用者因为创建逻辑导致的错误
简单工厂设计模式
定义一个接口
定义两个水果类
创建工厂
工厂方法设计模式
定义一个接口
public interface Fruit {
void show();
}
定义两个水果类
public class Apple implements Fruit {
@Override
public void show() {
System.out.println("苹果");
}
}
public class Pear implements Fruit {
@Override
public void show() {
System.out.println("梨");
}
}
创建FruitFactory接口
public interface FruitFactory {
Fruit createFruit();
}
用到什么类, 写什么工厂
AppleFactory
public class AppleFactory implements FruitFactory{
@Override
public Fruit createFruit() {
return new Apple();
}
}
PearFactory
public class PearFactory implements FruitFactory{
@Override
public Fruit createFruit() {
return new Pear();
}
}
使用
public class Test {
public static void main(String[] args) {
AppleFactory appleFactory = new AppleFactory();
PearFactory pearFactory = new PearFactory();
Apple apple = (Apple) appleFactory.createFruit();//获得苹果
apple.show();
Pear pear = (Pear) pearFactory.createFruit();//获得梨
pear.show();
}
}
包装类与工具类
包装类
什么是包装类
在面向对象当中“一切皆对象”
基本数据类型变量不是对象,比如 int a = 10; 当中就没有对象,所以就很矛盾
此时我们就可以对象基本数据类型进行包装,把基本数据类型包装一个对象。
把基本数据类型变的更强大,以面向对象的思想来去使用这些类型。
对基本数据类型包装的好处
使用包装对象后,功能变的更加强大。 以前使用double来去表示一个人的分数 此时这个人的分数考了0分,可以表示0.0 如果这个人没有来考试,那就代表这个没有分数 使用包装类型后, 即
可表示这两种状态一个0.0 一个 是null 基本数据类型就办不到。 包装类当中给我们提供了很多方法,可以直接过来使用 我们要将一个数据转成二进制
使用包装对象后, 就可以直接调用方法算出来 不需要我们再去手动去算。 对基本数据类型进行了增加 —> 每一个基本数据类型都对应一个类 有了类之后, 可以以对象的方式来使用这些类型
装箱操作
概念
把基本数据类型变成 包装类
Integer num = new Integer(10);
Integer num2 = Integer.valueOf(20);
System.out.println(num.MAX_VALUE);
System.out.println(num.MIN_VALUE);
System.out.println(num.TYPE);//知道是有什么基本数据类型包装过来的
double b = 13.5;
Double num3 = new Double(b);
Double num4 = Double.valueOf(14.2);
System.out.println(num2);
int xq = 20;
Integer xq1 = new Integer(xq);
//可以把一个字符串转成Integer类型 字符串当中必须得是数字
Integer str = new Integer("123");
System.out.println(str);
//把整数 转成二进制
System.out.println(Integer.toBinaryString(5));
Double d = new Double(10.5);
System.out.println(d.SIZE);
基本数据类型对应的包装类
自动装箱
可以直接把一个基本数据 类型的值赋值给包装类对象。
示例
拆箱操作
概念
把包装类对象转成对应的基本数据类型我们称为拆箱。
//装箱操作:把一个基本数据类型的值,转换为对应的包装类对象。
Integer num1 = new Integer(10);
Integer num2 = Integer.valueOf(15);
//拆箱操作:把包装类的对象,转换为对应的基本数据类型的变量
int num3 = num2.intValue();
System.out.println(num2);
System.out.println(num3);
Double d = Double.valueOf(10.5);
double d1 = d.doubleValue();
Long l = new Long(100);
long l1 = l.longValue();
自动拆箱
可以直接把一个包装类对象赋值给基本数据类型的变量
示例
字符串与基本数据类型和包装类型转换
包装类valueof缓存设计
当使用valueof创建一个包装类,它内部的实现会先从缓存当中查看是否已经有该存在匹配条件 的值
如果有就直接返回,不会再去创建新的地址。如果没有,就去创建新的地址。
main参数
当点击运行时,JVM自动会调用main方法
public : 被JVM调用的方法 ,它的权限要足够的大。
static : 被JVM调用的方法, 不需要创建对象,直接使用类名调用
void : 被JVM调用的方法,不需要有任何 的返回值。
main : 方法 的名称 ,只能这样写,不然JVM识别不了
String[] args : 运行时传递的参数
System类
Scanner类
作用
等待从键盘录入一个数
示例
System
拷贝数组
arraycopy
int[] src = {1,2,3,4,5,6};
int[] dest = new int[10];
//从src数组当中第二个角标位置开始复制
//共复制4个
//放到dest数组当中第1个位置
System.arraycopy(src, 2, dest, 1, 4);
System.out.println(Arrays.toString(src));
System.out.println(Arrays.toString(dest));
参数
src - 源数组。
srcPos - 源数组中的起始位置。
dest - 目标数组。
destPos - 目标数据中的起始位置。
length - 要复制的数组元素的数量。
终止当前正在运行的 Java 虚拟机
System.exit(0); 正常退出
System.exit(1); 非正常退出
运行垃圾回收器
gc
System.gc();
//当一个对象被垃圾回收时,自动调用的一个方法
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("我被 回收了");
}
Math类
求最大值Math.max(a,b)
//求两个数的最大值
int res1 = Math.max(10, 20);
System.out.println(res1);
求两个数的最小值
int res = Math.min(5, 6);
System.out.println(res);
返回一个[0 1) 随机数
//返回一个[0 1) 随机数
Math.random();
//返回0到100之间的随机整数
int res2 = (int)(Math.random()*100);
System.out.println((Math.random()));
开根
//开根
double res3 = Math.sqrt(4);
System.out.println(res3);
大精度小数BigDecimal
金额用什么数据类型
不是 doube,更不是 float ,而是用 BigDecimal
对于金融项目,对于金额,误差是不能容忍的
Java 中 float 的精度为 6-7 位有效数字。double 的精度为 15-16 位,
BigDecimal 用来对超过16位有效位的数进行精确的运算
常用方法
add(BigDecimal)BigDecimal对象中的值相加,然后返回这个对象。
subtract(BigDecimal)BigDecimal对象中的值相减,然后返回这个对象。
multiply(BigDecimal)BigDecimal对象中的值相乘,然后返回这个对象。
divide(BigDecimal)BigDecimal对象中的值相除,然后返回这个对象。
toString()将BigDecimal对象的数值转换成字符串。
doubleValue()将BigDecimal对象中的值以双精度数返回。
floatValue()将BigDecimal对象中的值以单精度数返回。
double d1 = 11000;
double d2 = 0.35;
// 错误的:3849.9999999999995
System.out.println("错误的:" + d1 * d2);
BigDecimal bigDecimal1 = new BigDecimal(11000);
BigDecimal bigDecimal2 = BigDecimal.valueOf(0.35);
// multiply 乘法;正确的:3850.00
System.out.println("正确的:" + bigDecimal1.multiply(bigDecimal2));
String类
什么是字符串?
把多个字符串连一起
字符串本质
在JDK9.0之前
其它是一个char[]数组
private final char value[];
根据UTF-16,JAVA的内部编码格式,编译成了Class文件,然后送给JVM执行,
一个UTF-16 字符占2个字节,不管是什么样的char都是2个字节
存在的问题
JDK8的字符串存储在char类型的数组里面,
在大多数情况下,char类型只需要一个字节就能表示出来了,
比如各种字母什么的,两个字节存储势必会浪费空间,
JDK9的一个优化就在这,内存的优化
JDK9.0
private final byte[] value;
JDK9是由byte类型的数组来进行存储的
在JDK9的String类中,维护了这样的一个属性coder,它是一个编码格式的标识,使用LATIN1还是UTF-16
String创建
1.直接赋值 String str = “myxq”;
2.通过构造器来创建 String str2 = new String(“myxq”);
字符串的比较
- == 比较两个内存地址是否相等
- 使用equals 它是在object 和 == 相同
字符串对象值为空
1.表示引用为空
String str = null; 还没有初始化,没有分配内存空间
2.表示空字符串
String str = “”; 已经创建了对象,已经分配了内存,内容为空
字符串常用方法
//把一个char数组可以转换成字符串
char[] cs = new char[] {'A','B','C'};
String str = new String(cs);
System.out.println(str);
//返过来也是可以的(把一个字符串转成char类型的数组)
char[] cs2 = str.toCharArray();
//获取字符串当中的信息
String str2 = "myxq";
//返回字符串长度
System.out.println(str2.length());
//获取字符串当中的某一个字符
System.out.println(str2.charAt(2));
//返回一个子字符串在字符串当中 的第一次出的一个位置
String str3 = "ABCDEFCDGCD";
String str4 = "CD";
字符串分类
-
不可变字符串String
定义好之后,就不能再去改变了 (内存地址不可变) -
可变的字符
定义好之后,还可以进行修改 改变时,不会创建新的内存地址
StringBuilder
StringBuffer
可变字符串
StringBuilder
是没有 synchronized 效率高一些
StringBuffer
方法前面多了一个synchronized 加锁 更安全 效率低了一些
效率测试
static void testString(){
long begin = System.currentTimeMillis();
String str = "";
for(int i = 0; i <= 10000; i++) {
str += i;
}
long last = System.currentTimeMillis();
long res = last -begin;
System.out.println(res);
}
static void testBuilder() {
long begin = System.currentTimeMillis();
StringBuilder str = new StringBuilder();
for(int i = 0; i <= 10000000; i++) {
//str += i;
str.append(i);
}
long last = System.currentTimeMillis();
long res = last -begin;
System.out.println(res);
}
static void testBuffer() {
long begin = System.currentTimeMillis();
StringBuffer str = new StringBuffer();
for(int i = 0; i <= 10000000; i++) {
//str += i;
str.append(i);
}
long last = System.currentTimeMillis();
long res = last -begin;
System.out.println(res);
}
示例
//StringBuilder 创建方法的使用
//创建的可变字符串,初始容量为16
//如果超过的话,它会自动扩容
//可变字符串,本质 还是一个char类的数组
StringBuilder sb = new StringBuilder("myxq");
//StringBuilder sb2 = new StringBuilder(16);
System.out.println(sb.capacity());
//链式编程
sb.append("abcdefg");
sb.append("123");
System.out.println(sb);
//删除指定位置 的字符
sb.deleteCharAt(1);
System.out.println(sb);
//可变字符转成不可变
String s = sb.toString();
//字符串的反转
System.out.println(sb.reverse());
Random
Random类
Random r = new Random();
int res = r.nextInt();
System.out.println(res);
System.out.println(r.nextDouble());
System.out.println(r.nextBoolean());
//相同的种子,生成的随机数是一样
Random r2 = new Random(110);
System.out.println(r2.nextInt());
生成34到179之间的随机数
//生成34到179之间的随机数
//34 + [0 145)
//nextInt(145) 随机生成0 到145之间的随机数
new Random().nextInt(145);
System.out.println("--------------");
生成5位随机数
//生成验证码
//5位的随机数 UUID生成的是16进制
String res = UUID.randomUUID().toString();
System.out.println(res);
res = res.substring(0,5);
System.out.println(res);
System.out.println("--------------------");
UUID
//UUID:通用唯一识别符
//在一台机器 上生成 的数字
//当前的时间,跟当前电脑网卡 生成一段字符
String uuid = UUID.randomUUID().toString();
System.out.println(uuid);
生成验证码
String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
str = str + str.toLowerCase();
str += "0123456789";
System.out.println(str);
System.out.println(str.length());
//从所有的字符当中随机生成5个出来
//随机取5个出来
//每取出一个结果,在原来的基础 上面进行拼接
StringBuilder sb = new StringBuilder(5);
for(int i = 0; i < 5; i++) {
//角标要随机的值 (0 62:字符串的长度)
int index = new Random().nextInt(str.length());
char ch = str.charAt(index);
sb.append(ch);
}
Date日期类
创建日期
//创建一个日期对象
Date date = new Date();
System.out.println(date);
//获取当前时间 的毫秒
long curTime = System.currentTimeMillis();
//把一个毫秒值 转成日期类型
Date date2 = new Date(curTime);
System.out.println(date2);
//中国人喜欢风格
String str = date2.toLocaleString();
System.out.println(str);
//把一个日期类型转成毫秒值。
System.out.println(date2.getTime());
日期格式化
//日期格式化
Date date = new Date();
System.out.println(date);
System.out.println(date.toLocaleString());
//获取为日期和时间使用 SHORT 风格的默认日期/时间格式器。
DateFormat df = DateFormat.getInstance();
//对指定 的日期进行格式化
String time = df.format(date);
System.out.println(time);
日期类型转成字符串
//把一个日期类型转成字符串
String res = DateUtil.dateToString(new Date(),"yyyy-MM-dd hh:mm:ss");
System.out.println(res);
String res2 = DateUtil.dateToString(new Date(),null);
System.out.println(res2);
String res3 = DateUtil.dateToString(new Date());
System.out.println(res3);
给一个字符串可以转成日期
public static Date StringToDate(String str, String pattern) throws ParseException {
// 判断传入的模式是否为空
if (StringUtils.isBlank(pattern)) {
// 为空的话,设置一个默认的模式
pattern = DEFAULT_PATTERN;
}
SimpleDateFormat sd = new SimpleDateFormat();
sd.applyPattern(pattern);
Date res = sd.parse(str);
return res;
}
自定义日期模式
//自定义日期模式
Date date = new Date();
SimpleDateFormat sd = new SimpleDateFormat();
//定义自己想要什么 样的模式
String pattern = "yyyy-MM-dd HH/mm/ss";
sd.applyPattern(pattern);
//以指定的模式格式化哪个日期
String res = sd.format(date);
System.out.println(res);
集合框架
集合
集合:就是有来存放数据 的一个容器
Java提供集合类:
什么时候使用数组,什么时候使用集合类
* 1.如果元素个数是固定,推荐使用数组
* 2.如果元素不是固定,推荐使用集合
数组和集合区别?
*1.数组能存基本数据类型,和 引用类型
* 2.集合当中只能存放引用数据类型,直接放,也会自动帮你装箱(把基本数据类型转成对象) 集合当中只能存放对象
* 3.数组长度是固定,不能再去增长
* 集合长度是可以改变,根据元素的增长而增加
什么时候使用数组,什么时候使用集合类
1.如果元素个数是固定,推荐使用数组
2.如果元素不是固定,推荐使用集合
集合继承结构图
Collection是一个接口,真正使用的是它的实现类
集合通用方法
- 添加
- 删除元素
- 判断一个集合是否为空
- 获取集合当中 的长度
- 清空集合当中所有的内容
- 把c2当中的所有元素合并到c1当中
- 从c1删除两个集合的交集
- 判断调用的集合是否包含(全部包含)传入集合
- 取交集 把交集的结果赋值给调用者
集合的遍历
集合遍历
迭代器
Collection arrayList = new ArrayList();
arrayList.add("a");
arrayList.add(1);
arrayList.add("c");
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()){
System.out.println( iterator.next());
}
List集合
list中添加,获取,删除元素
//list中添加,获取,删除元素
List<String> person=new ArrayList<>();
person.add("jackie"); //索引为0 //.add(e)
person.add("peter"); //索引为1
person.add("annie"); //索引为2
person.add("martin"); //索引为3
person.add("marry"); //索引为4
person.remove(3); //.remove(index)
person.remove("marry"); //.remove(Object o)
遍历
迭代器遍历
并发修改异常
修改异常原因
解决并发修改异常
ListIterator迭代器
给我一个List集合,把里面重复的元素去除
LinkedList
遍历
特有方法
泛型
List中添加元素存在的问题
往集合当中存储元素,可以存任何类型元素
//1.往集合当中存储元素,可以存任何类型元素
List list = new ArrayList();
list.add(1);
list.add("abc");
//取出来全是Object
Object obj = list.get(1);
//现在想要使用字符串当中 特有的方法 ,必须得要转回来
String str = (String)obj;
str.substring(0, 1);
泛型概念
什么 是泛型
广泛通用 的类型
一开始还不确定是什么类型,在使用的时候,才能确定是什么类型
1.在开始定义类的时候,留一个插口
2.在创建对象的时候,再去插入对应的类型
泛型定义
泛型注意点
1.泛型前后类型必须得要保持一致
2.从JAVA7开始,后面的类型可以不写 new ArrayList<>(); 菱形语法
3.泛型是没有继承
4.泛型其实是一个语法糖。(本质还是Object 内部其实 还是要做强转)
没有使用泛型的List
泛型类
在类上面定义的泛型,在创建对象的时候,要指明泛型的类型
泛型当中定义 的泛型只能用在普通方法上面
不能使用在静态方法 上面
静态方法是直接使用类名调用,泛型是在创建对象的时候,才去指定类型
示例
泛型方法
就是在方法 上面添加了泛型
单独对一个方法上面声明泛型
方法当中定义的泛型
示例
自定义泛型方法
通配符
不知道使用什么类型来接收的时候,可以使用?表示未知
通配符只用来做接收使用
不能做添加操作
示例
泛型的上限
泛型的上限:用来限定元素的类型必须得指定类型(Number)的子类<或者是指定类型或者指定类的子类>
示例
泛型的下限
用来限定元素的类型必须得指定类型(Number)的父类(或者是指定的类)
示例
泛型擦除(把泛型给去掉)
把一个有泛型集合 赋值给了一个没有泛型的集合 最后就没有 泛型->泛型擦除(把泛型给去掉)
数组与集合的转换
把数组转成集合
引用数据类型的数组才去转成集合
集合转数组
Set
特点
Set当中存的元素是无序,里面没有重复的元素
示例
获取1到20之间随机数
抽出一个方法来
传的数字是几 就生成几个不重复的随机数
LinkedHashSet
特点
LinkedHashSet它是HashSet子类 它底层是使用链表实现,是Set集合当中,唯一的一个保证元素是怎么存怎么取的
HashSet 能够保证元素的唯一
Map
概念
概念
映射关系
A集合 B集合 (ArrayList LinkedList Vector HashSet LinkedHashSet TreeSet)
A集合当中 的每一元素,都 可以在B集合当中找到一个唯一的一个值与之对应
A集合当中 的元素不能是重复(Set)
A集合当中的每一个元素称它是一个key(键)
B集合当中的每一个元素称它是一个Value(值)
示例
基本操作
1.添加功能
2.删除功能
3.长度的功能
遍历map
方式1
Set<String> allKeys = map.keySet();
//取出每一个key 获取对应的value值
Iterator<String> it = allKeys.iterator();
while(it.hasNext()) {
//取出key值
String key = it.next();
Object val = map.get(key);
System.out.println(key+"="+val);
}
System.out.println("------------");
//set能使用迭代器,就能使用foreach
for (String key : map.keySet()) {
System.out.println(key+"="+map.get(key));
}
方式2
//双列集合
Map<String, Integer> map = new HashMap<>();
map.put("张三", 20); //每一个put的key-value 就是一个entry对象
map.put("李四", 21);
map.put("王五", 22);
//获取所 有的key-value对象 entry对象
//Entry是定义 在map内部当中的一个接口
//获取所有 的entry对象
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
//遍历每一个entry对象
Iterator<Entry<String, Integer>> it = entrySet.iterator();
while(it.hasNext()) {
//取出每一个entry对象
Entry<String, Integer> en = it.next();
//取出entry对象的key
String key = en.getKey();
//取出entry对象的值
Integer value = en.getValue();
System.out.println(key +" = " + value);
}
System.out.println("--------------");
for (Entry<String, Integer> entry : entrySet) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
LinkedHashMap
//使用HashMap它的key是没有顺序
HashMap<String,Integer> hm = new HashMap<>();
hm.put("张三", 20);
hm.put("李四", 20);
hm.put("王五", 20);
System.out.println(hm);
//添加的元素是有顺序(你怎么放的,打印时就是什么顺序的)
LinkedHashMap<String,Integer> hm2 = new LinkedHashMap<>();
hm2.put("张三", 20);
hm2.put("李四", 20);
hm2.put("王五", 20);
System.out.println(hm2);
异常
异常概述
什么是异常
Java在运行过程当中出现的错误
异常继承体系
Throwable
Throwable继承体系
异常处理方式
1.自己处理,然后继续运行
2.自己没有办法处理,交给Jvm来处理 JVM有一个默认的异常处理机制将异常处理 并将该异常的名称,异常信息,异常出现的位置给打印在控制台上,同时停止运行
10/0; 违背了算法运算法则,抛出异常 抛出的异常是一个对象 New ArithmeticException(“/by zero”)
把这个异常对象返回 此时想把异常对象赋值给a a是一个int类型不能接收,main方法解决不了
交给jvm来处理,jvm就将该异常在控制台打印程序终止。
自己处理异常
try…catch…finally
基本格式
try{
可能出现理异常的代码
}catch(异常类型){
}finally{
处理完异常最后做的处理
}
Try:用来检测异常
Catch:用来捕获异常
Finally:释放资源
10/0;会创建一个异常对象
New ArithmeticException(“/by zero”)
赋值给a,接收不了,此时就把异常对象传给catch当中的参数a
能够接收,就会执行catch当中的内容,程序也没有终止
只有在try出问题的时候才会执行catch当中的内容
出问题时 catch 处理完毕之后,后续的代码继续执行
Try…catch处理多个异常
在try当中可能会出现不同的异常类型
此时可以使用多个catch来进行处理
Exception e不能写在最上面,写在上面会报错,因为下面的永远不会执行到
JDK7处理多个异常
常用方法
获取异常信息
获取异常类名和异常信息
获取异常类名和异常信息,及异常出现的位置
throws方式处理异常
- 抛出运行时异常
抛出运行时异常,不需要处理
- 抛出编译时异常
抛出了一个编译时异常,必须得要有人处理
如果不处理,必须继续往上抛,抛给方法调用者
此时由于setAge内部抛出了一个异常,在调用该方法时必须得要处理
1.必须处理异常
2.继续往上抛出
throw与throws区别
throw
用在方法体内,跟的是异常对象名称
只能抛出一个异常对象
表示抛出异常
throws
用在方法声明后面,跟的是异常类名
可以跟多个异常类名,中间使用逗号隔开
表示抛出异常,由方法调用者来处理
编译时异常与运行时异常
什么是运行时异常
所有的RuntimeException类及其子类称为运行时异常,在编译时不会报错,在运行过程程序终止运行
什么是编译时异常
程序员必须显示处理,否则程序就会发生错误无法通过编译
编译时异常发生的情况
在编译时就必须处理这个异常
这个代码是读取一个文件,在读取时,它也不确定你这个文件是否存在
如果不存在,你要怎么处理,所以在编译时就要确定好
如果文件不存储该怎么样处理
运行时异常与编译时异常的区别
运行时异常,程序员犯的错误,需要回来修改代码
编译时异常在编译某个程序的时候,有可能会有这样那样的错误,
比如文件找不到,这样的异常就必须在编译时处理
如果不处理就编译不通过