前言:之前学了几遍Java,笔记做的不怎么样,现在想重新整理一下,但是仍然有纰漏,欢迎指正!
基本语法
关键字和保留字
关键字(keyword)
定义
被Java语言赋予了特殊含义,用作专门哦那个图的字符串
特点
所有关键字中所有字母都是小写的
保留字(reserved word)
定义
现有的Java版本尚未使用,但是以后版本呢可能会作为关键字使用。
goto
、const
标识符(identifier)
定义
Java 对各种变量、 方法和类等要素命名时使用的字符序列称为标识符。
标识符定义规则:
- 由26个英文字母大小写, 0-9 , _或 $ 组成
- 数字不可以开头。
- 不可以使用关键字和保留字,但能包含关键字和保留字。
- Java中严格区分大小写,长度无限制。
- 标识符不能包含空格。
标识符提示
-
凡是自己可以起名字的地方都叫标识符。
-
Static也可以作为标识符,因为Java严格区分大小写
名称命名规范
命名规范:
- 包名:多单词组成时所有字母都小写: xxxyyyzzz
- 类名、接口名:多单词组成时,所有单词的首字母大写: XxxYyyZzz
- 变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始每个
单词首字母大写: xxxYyyZzz - 常量名:所有字母都大写。多单词时每个单词用下划线连接: XXX_YYY_ZZZ
注意事项:
-
不强制遵循这个规则(即使不按照这个规则也不会报错),但是尽量采用这种命名规则增加可读性,易用性(调用类的时候可以不看就知道怎么写)
-
起名的时候要做到“见名知意”,即见到名字就知道是什么意思
-
由于Java采用Unicode编码,故标识符可以使用用汉字,但是实际使用应该避免使用汉字
变量
概念
- 内存中的一个存储区域
- 该区域的数据可以在同一类型范围内不断变化
- 变量是程序中最基本的存储单元。包含变量类型、变量名和存储的值
作用:
用于在内存中保存数据
声明变量
- 语法: <数据类型> <变量名称>
例如:int var;
变量的赋值
- 语法: <变量名称> = <值>
例如:var = 10;
声明和赋值变量
- 语法: <数据类型> <变量名> = <初始化值>
例如:int var = 10;
变量声明使用实例:
/**
* @author wangxin
* @create 2020-07-23 10:04
*/
public class Main {
public static void main(String[] args) {
//声明并赋值
int age = 10;
System.out.println(age);
//先声明再赋值
int num;
num = 5;
System.out.println(num);
//若只声明不赋值会报错
// int charge;
// System.out.println(charge);
}
}
变量的类型:
分类:
注意其中字符串为引用数据类型
汇总表:
字节换算:
例如byte,有一个字节,1字节=8bit ,1bit能存储一个二进制数,所以能存储最大的数位(二进制全为1),即为255,最小能存储(二进制全为0),即为0,因为实际使用含负数,所以取中间的范围,即为-128到+127
1MB = 1024KB 1KB= 1024B 1B=8bit
bit: 计算机中的最小存储单位。 byte:计算机中基本存储单元。
整形:
-
byte超过定义大小会报错,注意编译时,运行时byte值的大小
-
long以的l结尾,通常采用大写的L,因为l和1很像,可以避免出错
-
平常编程中通常使用较多的是int
浮点型
-
浮点比整形存的数据范围更大,因为采用次幂存储,没有具体到每一位,故节省空间,存的范围更大
-
由表中得出double和float的精度不能非常高(float:小数点后7位,double:小数点后14位),需要高精度的浮点需要引用其他类的支持(见后)。
-
定义float要以f结尾,大小写都可以
-
编程中通常使用double
字符类型
-
采用单引号包住字符
-
只能包一个字符(两个字符会报错)
-
字符里面也可以放转义字符,转义字符如下表
- 字符里面可以放Unicode码(16进制)表示单个字符
Unicode字符集是把字符与二进制互转的对应关系表,因为计算机底层采用二进制存储数据,所以字符集类似函数中的函数,二进制和字符类似于自变量和因变量(可互换,因为谁当自变量因变量取决于怎么转换)
布尔类型
-
只能取true,false(不带任何符号)
-
常用于条件判断循环等中使用
变量类型实例:
/**
* @author wangxin
* @create 2020-07-23 10:04
*/
public class Main {
public static void main(String[] args) {
//1.byte正常使用:10
byte b1 = 10;
System.out.println(b1);
//2.byte超过范围,报错
// byte b2 = 128;
//3.long正常使用:1234567891044455455
long l1 = 1234567891044455455L;
System.out.println(l1);
//4.float正常使用:2.5
float f1 = 2.5f;
System.out.println(f1);
//5.不会报错,但是超过精度的数据会被截取,此输出为:2.1234567
float f2 = 2.123456789f;
System.out.println(f2);
//6.double正常使用:3.1415926
double d1 = 3.1415926;
System.out.println(d1);
//8.char正常使用:w 王 わ
char c1 = 'w';
System.out.println(c1);
char c2 = '王';
System.out.println(c2);
char c3 = 'わ';
System.out.println(c3);
//9.正常使用,转义符:'
char c4 = '\'';
System.out.println(c4);
//10.正常使用,Unicode码:王
char c5 = '\u738b';
System.out.println(c5);
//11.如果括号里面不包含任何值会报错
//char c6 = '';
//12.布尔类型正常使用:true
boolean ble1 = true;
System.out.println(ble1);
//13.布尔类型错误使用,不能待任何符号
// boolean ble2 = 'true';
}
}
变量使用作用域:
定义:
变量在程序中有它存在的有效位置,超出这个位置则变量无法使用。
变量作用域实例:
/**
* @author wangxin
* @create 2020-07-23 10:04
*/
public class Main {
public static void main(String[] args) {
//1.声明变量age且可以访问age
int age = 10;
System.out.println(age);
//3.再次声明age无法声明,因为age在此作用域内有效,再次声明变量重复,报错
//int age = 20;
}
public static void method(){
//2.无法访问age,因为age是另外一个函数的变量,超出了age的作用域
//System.out.println(age);
}
}
变量使用注意事项总结:
-
Java中每个变量必须先声明,后使用
-
Java为强类型语言必须声明变量类型,JavaScript、python为弱类型可不用声明变量类型
-
使用变量名来访问这块区域的数据
-
变量的作用域:其定义所在的一对{ }内
-
变量只有在其作用域内才有效
-
同一个作用域内,不能定义重名的变量
Tips:逆向思维,有些时候遇到不确定的问题,可以先假设成立,然后代码验证正确与否
自动类型提升
定义:
有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算。
其中无boolean类型,因为布尔类型无法做运算
自动转换关系:
-
由图中得出byte,char,short处于同级关系,故不能相互转化(自身转化也不行),都只能自动转化为int
-
级别低和级别高的运算的结果自动往级别高的靠,且只能拿级别高的变量类型来装
-
java在做运算的时候,如果操作数均在int范围内,那么一律在int的空间内运算
自动提升实例:
/**
* @author wangxin
* @create 2020-07-23 10:04
*/
public class Main {
public static void main(String[] args) {
//1.正常类型转换:30
byte b1 = 10;
int i1 = 20;
int i2 = i1 + b1;
System.out.println(i2);
//2.采用同级的变量类型,或者更低的变量类型,报错
//byte b2 = b1+i1;
//3.同级别的变量不能装(byte,short,char),报错
// byte b3 = 10;
// byte b4 = 10;
// byte b5 = b3+b4;
}
}
强制类型转换
定义:
自动类型转换的逆过程, 将容量大的数据类型转换为容量小的数据类型。
- 使用时要加上强制转换符:()
- 但可能造成精度降低或溢出,格外要注意。
当定义int为128时,强制转换为byte,其大小超过了byte,最后输出-128
-
long类型的变量没有加L,转为int,数据的大小超过int会报错(整型默认为int类型)
-
float后面不加f会报错(浮点型数据默认为double类型)
强制类型转换实例:
/**
* @author wangxin
* @create 2020-07-23 10:04
*/
public class Main {
public static void main(String[] args) {
//1.正常转换:100
int i1 = 100;
byte b1 = (byte) i1;
System.out.println(b1);
//2.正常转换,精度溢出:3
double d1 = 3.14;
int i2 = (int) d1;
System.out.println(i2);
//3.long类型的变量没有加L,数据的大小超过int会报错(整型默认为int类型)
//long l1 = 12345678910; 报错
//正确
long l2 = 12345678910L;
//4.float后面要加f,不然会报错(浮点数默认为double类型的)
//float f1 = 3.14;
}
}
字符串类型
定义:
用于存储字符串的数据类型,本质是引用数据类型
-
使用双引号,内容不写也可以,也可以只写一个字符。
-
当把任何基本数据类型的值和字符串(String)进行连接运算时(+), 基本数据类型的值将自动转化为字符串(String)类型
练习题:
String str1 = 4; //判断对错: no,String不能存储 整型数据
String str2 = 3.5f + “”; //判断str2对错: yes 类型自动转换为String
System.out.println(‘a’+1+“Hello!”); //输出: 98Hello! 前面char和int相加结果为int,再和String相加
字符串使用实例
/**
* @author wangxin
* @create 2020-07-23 10:04
*/
public class Main {
public static void main(String[] args) {
//1.String正确的使用方式,都可以正常输出
String s1 = "";
String s2 = "王";
String s3 = "\n";
String s4 = "陪伴是最长情的告白!";
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);
System.out.println(s4);
//2.String与其他数据类型相加,自动转换为String类型
String s5 = "学号:";
int i1 = 10;
String s6 = i1+s5;
System.out.println(s6);
}
}
其中String转其他基本类型详见之后的String的包装类使用
进制之间的转换
-
所有数字在计算机底层都以二进制形式存在。
-
对于整数而言,有四种表示方式:
- 二进制(binary): 0,1 ,满2进1.以0b或0B开头
- 十进制(decimal): 0-9 ,满10进1。
- 八进制(octal): 0-7 ,满8进1. 以数字0开头表示
- 十六进制(hex): 0-9及A-F,满16进1. 以0x或0X开头表示。此处的A-F不区分大小写。
如: 0x21AF +1= 0X21B0
-
常用进制如下表
二进制简介:
- Java整数常量默认是int类型,当用二进制定义整数时,其第32位是符号位;当是long类型时,二进制默认占64位,第64位是符号位
- 二进制的整数有如下三种形式:
- 原码:直接将一个数值换成二进制数。最高位是符号位
- 负数的反码:是对原码按位取反,最高位(符号位)确定为1。
- 负数的补码:其反码加1(末尾加1,如果本来为1需要进位)。
- 计算机以二进制补码的形式保存所有的整数。
- 正数的原码、反码、补码都相同
- 负数的补码是其反码+1
为什么要使用原码、反码、补码表示形式呢?
计算机辨别“符号位”显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法. 我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法(只需要原酸加法), 这样计算机运算的设计就更简单了。
二进制的保存
问题:当int类型的数据128转换为byte类型时为什么结果为-128?
因为int是4字节的存储方式,byte的1字节的存储方式,当int的128转换为byte时超过了1位的大小,故byte只截取一位的数据,导致符号位和大小改变。
双字节转换为byte 截取最后,符号位改变
二进制转十进制
十进制转二进制
其它进制的转换
以二进制作为中间变量进行转换
运算符
- 算术运算符
- 赋值运算符
- 比较运算符(关系运算符)
- 逻辑运算符
- 位运算符
- 三元运算符
算数运算符
加法运算符
- 对于整型和浮点型而言,等同于数学计算中的加法
- 对于字符串而言,用于连接字符串
减法运算符
- 普通计算中的减法
乘法运算符
- 普通计算中的乘法
除法运算符
- 普通计算的除法
- 注意当除数和被除数数据类型为整数类型时,结果也为整型
- 当除数和被除数任一有一个为浮点型数据时,会自动转化为浮点结果(数据类型的自动向上转换)
- 当除数与被除数都是整形时,注意除数为零会引发异常(程序出错),要避免
求余运算符
- 求余的结果不一定是整数,也可能是小数(5(int)%3.3(double))
- 当第一个操作数与第二个操作数都是整形时,注意第二个操作数为零会引发异常(程序出错),要避免
- 如果某一个操作数为浮点数,当第二个操作数为0时,结果为NaN
- 第一个操作数为0(0.0)和任何数求余都是0(0.0)
- 求余结果的符号取决于第一个操作数
自加运算符(++)
- 自加是单目运算符,只能运算一个变量
- 自加只能操作单个数值(整型、浮点型)的变量,不可以操作常量或表达式
- 自加运算符可以出现在变量的左边和右边,其效果不一样
- 左边(++var):先把变量+1,再把变量放进表达式中运算
- 右边(var++):先把变量放进表达式中运算,再把变量+1
自减运算符(–)
同自加运算符
思考:byte a=127; 那System.out.println(++a);输出的值为多少?
答:当执行输出语句的时候,++a先执行自加,然后再输出,我们知道byte最大为127,自加操作已经超出了byte的大小,这个时候程序在int运算区域(4字节)内运算,当输出的时候,自动截取int内的(1字节),导致数值和符号位改变,最后输出为-128,更具体的原因请看上
/** * @author wangxin * @create 2020-07-24 9:08 */ public class Main { public static void main(String[] args) { byte a = 127; System.out.println(++a); } }
算数运算符实例
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
int a = 30;
int b = 20;
double c = 30.3;
double d = 25.5;
String e = "陪伴是";
String f = "最长情的告白!";
//整型变量相加:50
System.out.println(a+b);
//浮点型变量相加:55.8
System.out.println(c+d);
//字符串相加:陪伴是最长情的告白!
System.out.println(e+f);
//整型变量相减:10
System.out.println(a-b);
//浮点型变量相减:4.800000000000001
System.out.println(c-d);
//整型变量相乘:600
System.out.println(a*b);
//浮点型变量相乘:772.65
System.out.println(c*d);
//整型变量相除,后面部分会被截取,不是四舍五入:1
System.out.println(a/b);
//浮点型变量相除,具有小数部分:1.188235294117647
System.out.println(c/d);
//当除数和被除数中有只要有一个为浮点型数据时,结果为浮点型:0.9900990099009901 1.01
System.out.println(a/c);
System.out.println(c/a);
int g = 0;
//除数不可以为零,报错: by zero
//System.out.println(a/g);
double h = 0.0;
//当除数是浮点型为零时,不会报错,结果为:Infinity
System.out.println(a/h);
//整型求余 :10
System.out.println(a%b);
//浮点型求余:4.800000000000001
System.out.println(c%d);
//当dierge数是浮点型为零时,不会报错,结果为:NaN
System.out.println(a%h);
int x1 = 10;
int y1 = 20;
//自增在前,先运算,再带入表达式 x1 = 11 x2 = 11
int x2 = ++x1;
System.out.println("x1 = " + x1);
System.out.println("x2 = " + x2);
//自增在后,先带入表达式,再运算 y1 = 21 y2 = 20
int y2 = y1++;
System.out.println("y1 = " + y1);
System.out.println("y2 = " + y2);
}
}
赋值运算符
作用:用于为变量指定变量值
-
可以连续赋值
/** * @author wangxin * @create 2020-07-24 9:08 */ public class Main { public static void main(String[] args) { //单个整型赋值 int a = 100; //单个浮点赋值 double b = 2.5; //单个字符串赋值 String c = "陪伴是最长情的告白!"; //单个布尔赋值 boolean d = true; //多个整型赋值,e,f,g都为10 int e; int f; int g; e = f = g = 10; } }
-
取反运算只适用于单个操作数,它直接将计算机底层二进制码按位取反(包括符号位)。
位运算举例1(5^9)
- 5的二进制码是0101(0是为了对齐)
- 9的二进制码是1001
- 由运算法则中的异或运算法则得结果为 1100,为10进制的12,得到最后结果
问:为什么是直接用二进制码计算的?
答:因为正数的原码和补码是相同的,故直接拿原码计算即可(底层并不是,自己手算可以这样)
问:为什么前面有这摩多0?
答:8bit=1字节 int占4字节 故有32bit,32个存储0、1的单元,前面没有值,故为0,方便计算,只取有效部分
位运算举例2(~-5)
- 首先得出-5的反码
- 再得出-5的补码
- 最后把补码取反,得到结果
注意:其中取反以后符号位为0的话直接就是答案,如果符号位为1则需要逆运算得到原码才是最终结果!!!
位运算符举例3(左移运算符)
- 5 << 2 (5向左移动2位)
- 把5的二进制(000…0101)码向左移动两位(包括符号位,0…010100)
- 得到新的二进制码(10100),即为答案20
由于正数二进制原码补码都一样,且计算的时候符号位都没有改变,故直接用原码计算即可
- -5 << 2(-5向左移动2位)
- 把-5转换为补码(11111…111011)
- 向左移动两位(111…101100)
- 补码减1(二进制的1),恢复成反码(111…101011)
- 恢复成原码(100…010100),答案即为-20
提示:减1的时候遇到末尾为0的时候可能不好想,可以联想100-1=099 即是 100-001=011(二进制)
位运算符举例4(右移运算符)
- -5 >> 2 (有符号右移运算符)
- 把-5转换为补码(11111…111011)
- 把补码右移两位,前面空出的用符号位的数字填充(1111111…1110)
- 减一得到反码(1111111…1101)
- 取反得到原码(1000000…0010),得到答案-2
- -5 >>> 2 (无符号右移运算符)
- 把-5转换为补码(11111…111011)
- 把补码右移两位,前面空出的用符号位的0填充(0011111…1110)
- 减一得到反码(0011111…1101)
- 取反得到原码(0100000…0010),得到答案1073741822
注意:
- 对于低于int类型(入byte、short和char)的操作总是先转换为int再进行移位
- 对于int类型的整数移位a>>b,如果b>32的时候,系统会先对32求余(int类型的数据只有32位),然后得到的结果才是真正的位移数,例如,a>>33和a>>1,a>>32和a是结果是一样的
- 由上可得long类型由于是64位的,同理可得和上面的类似结论
- 移位运算不会改变变量,只是得到了一个结果
- a左移n位=a*(2^n) a右移n位=2/(2^n)
总结
拓展赋值运算符
作用
- 用于自身赋值运算
- 底层的原理和普通赋值不一样
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
short a = 10;
//这样写会报错
//a=a+1;
//拓展运算写法不会错
a+=1;
System.out.println("a = " + a);
}
}
- 拓展写法可以提升性能,且增加可读性,推荐!!!
比较运算符
作用
用于判断两个变量或者常量的大小,比较结果返回的是布尔值
分类
- 其中<,<=,>,>=与数学中的很类似,就不做介绍
==运算符
- 如果两个操作数都是数值类型,即使是类型不同,值相同,也返回true
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
int a = 10;
double b = 10.0;
//输出true
System.out.println(a==b);
int c = 97;
char d = 'a';
//字符a对应97,故输出true
System.out.println(c==d);
}
}
- 引用类型不可以和基本数据类型作比较
- 如果两个操作数为引用类型(例如String),必须两个类型具有父子关系(继承的知识)才能比较,并且需要指向同一个对象才能返回true
import java.util.Scanner;
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
String a ="陪伴是最长情的告白";
Object o = new Object();
//所有引用类型会继承Object类,所以可以比较,但是他们指向的对象不一样,所以输出false
System.out.println(o == a);
//引用的对象都是"陪伴是最长情的告白"这个字符串,返回true
System.out.println("陪伴是最长情的告白" == a);
String b = "陪伴是最长情的告白";
//即使是新建一个字符串,只要内容一样,那么引用的对象都一样,还是返回true
System.out.println(a == b);
String c = "陪伴是最长情的告白!";
//字符串内容不一样了,返回false
System.out.println(c == b);
Scanner sc1 = new Scanner(System.in);
Scanner sc2 = new Scanner(System.in);
//两个新建对象,引用的对象是不一样的,所以返回false
System.out.println(sc1 == sc2);
//把sc2的对象赋值给sc1,这个时候两个的引用对象都一样,所以返回true
sc1 = sc2;
System.out.println(sc1 == sc2);
}
}
提示:Sting字符串比较内容是否一样一般是不用==的,一般采用equals方法比较
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
String a = "陪伴是最长情的告白";
String b = new String("陪伴是最长情的告白");
//显示的字符串和a是一样的
System.out.println(b);
//比较结果是false,因为==比较的是引用的对象,这两个的引用对象是不一样的
System.out.println("a==b = " + (a == b));
//用equals方法比较字符串内容,得到true
System.out.println(a.equals(b));
}
}
!=运算符
- 如果两个操作数都是数值类型,即使是类型不同,只要值不同,就返回true
- 如果两个操作数为引用类型(例如String),必须两个类型具有父子关系(继承的知识)才能比较,并且只要指向对象不同就返回true
逻辑运算符
作用
用于操作两个布尔类型的常量或者变量
-
逻辑运算符用于连接布尔型表达式,在Java中不可以写成3<x<6,应该写成x>3 & x<6 。
-
“&”和“&&”的区别:
-
单&时,左边无论真假,右边都进行运算;
-
双&时,如果左边为真,右边参与运算,如果左边为假,那么右边不参与运算。
-
-
“|”和“||”的区别同理, ||表示:当左边为真,右边不参与运算。
不短路&
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
int a = 5;
int b = 10;
//运行结果为a = 5 , b = 11 ,b++可以得到运算
if(a>4 | b++ >10){
System.out.println("a = " + a);
System.out.println("b = " + b);
}
}
}
短路&&
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
int a = 5;
int b = 10;
//运行结果为a = 5 , b = 10 ,b++得不到运算
if(a>4 || b++ >10){
System.out.println("a = " + a);
System.out.println("b = " + b);
}
}
}
三目运算符
作用
用于判断,并且返回给某个值
格式
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
//判断5是否大于3,并且把字符串返回给变量,输出 5大于3
String str = 5>3 ? "5大于3" : "5不大于3";
System.out.println(str);
}
}
等价于
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
//判断5是否大于3
String str = null;
if(5>3){
str = "5大于3";
}else{
str = "5不大于3";
}
System.out.println(str);
}
}
- 三目运算符还支持嵌套
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
int a = 11;
int b = 11;
//三目运算符嵌套,判断是不是相等 ,输出 : a等于b
System.out.println(a > b ?
"a大于b" : (a < b ? "a小于b" : "a等于b")
);
}
}
运算符的优先级
运算符具有运算的优先级,优先级高的优先运算
优先级表
-
单目运算符、赋值运算符、三目运算符是从右至左运算的
-
不要把一个表达式写的太复杂,尽量使用"()"控制顺序
流程控制
作用
流程控制语句是用来控制程序中各语句执行顺序的语句,可以把语句组合成能完成一定功能的小逻辑模块。
- 顺序结构
- 分支结构
- 循环结构
顺序结构
定义
程序从上到下逐行地执行,中间没有任何判断和跳转。
注意:Java变量必须在前文有定义才能使用,不然会报错
分支结构
定义
根据条件,选择性地执行某段代码。
三种形式的if语句
- 放在if后面的必须是逻辑表达式(返回值为布尔类型)
- 如果代码块里面遇到了return、break、continue等关键字或者遇到了异常,代码后面的语句都会得不到执行
- 语句块只有一条执行语句时,一对{}可以省略,为了提高可读性和不容易出错,建议即使是一条执行语句也要写大括号
- if-else语句结构,根据需要可以嵌套使用
- 当if-else结构是“多选一”时,最后的else是可选的,根据需要可以省略
- 当多个条件是“互斥”关系时,条件判断语句及执行语句间顺序无所谓
- 当多个条件是“包含”关系时,“小上大下”
互斥:A和B的交集为空,即两个条件表达的范围没有重复,各自独立
包含:A和B具有交集,即两个条件表达的范围有重复(并不是完全重复,也可能只是一部分重复)。
小上大下:上面的是范围小的条件,往下逐渐增大范围
switch-case语句
语法
规则
- switch(表达式)中表达式的值必须是下述几种类型之一: byte, short,char, int, 枚举 (jdk 5.0及以后支持), String (jdk 7.0及以后支持);
- case子句中的值必须是常量,不能是变量名或不确定的表达式值;
- 同一个switch语句,所有case子句中的常量值互不相同,且常量值类型要相同和表达式的值类型相同;
- break语句用来在执行完一个case分支后务必使用break使程序跳出switch语句块;如果没有break,程序会顺序一直执行到switch结尾(执行过程中无论是不是等于那个常量,都会执行里面的语句)
- default子句是可有可无的。位置也可以灵活放置(一般建议放在最后)。当没有匹配的case时,执行default
循环结构
定义
根据循环条件,重复性的执行某段代码。
分类
- for 循环
- while 循环
- do-while 循环
循环语句的四个部分
- 初始化部分(init_statement)
- 循环条件部分(test_exp)
- 循环体部分(body_statement)
- 迭代部分(alter_statement)
for循环
语法
for (①初始化部分 ; ②循环条件部分 ; ④迭代部分){
③循环体部分;
}
执行流程
①-②-③-④-②-③-④-②-③-④-…-②
- ②循环条件部分为boolean类型表达式,当值为false时,退出循环
- ①初始化部分可以声明多个变量,但必须是同一个类型,用逗号分隔
- ④可以有多个变量更新,用逗号分隔
图解例子
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
for (int i = 1; i <= 100; i++) {
System.out.println(i);
}
}
}
tips:
-
变量污染问题:
变量定义的时候尽量不要把作用范围扩展的太大,这样容易误操作导致数据改变,得不到想要的结果
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
//作用域在整个函数内都有效
int i = 0;
for (; i < 10; i++) {
System.out.println(i);
}
}
}
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
//作用域仅仅在for循环内有效,用完自动销毁
for ( int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}
-
使用循环变量技巧
尽量不要直接使用循环变量,而是应该用一个临时变量去装循环变量,避免由于改变等操作导致循环变量的改变。
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
int tmp = 0;
for (int i = 0; i < 10; i++) {
tmp = i;
//这里可以访问i的值
System.out.println("tmp = " + tmp);
}
//还可以访问最后i的值
System.out.println("tmp = " + tmp);
}
}
- for循环的括号内可以不写循环初始化、条件部分(不写会导致死循环,但是不写不会语法报错)和迭代部分
- 对于标准for循环而言,循环条件总是要比迭代语句多执行一次
while循环
语法格式
①初始化部分
while(②循环条件部分){
③循环体部分;
④迭代部分;
}
执行过程
①-②-③-④-②-③-④-②-③-④-…-②
- 注意不要忘记写④(迭代部分)。 否则, 循环将不能结束, 变成死循环。
while使用例子
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
//初始化循环变量
int count = 0;
//当count<10的时候,执行循环体
while (count < 10){
//执行的语句
System.out.println("count = " + count);
//迭代语句
count++;
}
}
}
tips:
当while()的后面有分号的时候是一个死循环,请注意,这样分号相当于是一个空语句!
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
//初始化循环变量
int count = 0;
//分号是空语句,这是一个死循环
while (count < 10);
//不会得到执行,上面是一个死循环
{
System.out.println("count = " + count);
count++;
}
}
}
do-while循环
语法格式
①初始化部分;
do{
③循环体部分
④迭代部分
}while(②循环条件部分);
执行过程
①-③-④-②-③-④-②-③-④-…②
- 无论最开始循环条件是否成立,都会至少执行一次循环体
示例
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
//初始化循环变量
int count = 20;
//count的值已经大于10,返回的结果已经是假了,但是仍然会执行一次下面的输出语句
do {
System.out.println(count++);
}while (count < 10);
}
}
循环嵌套
定义:循环内又嵌套循环的结构
-
执行次数等于内层的循环次数(m)乘以外层的循环次数(n),即为m*n
-
只有当内层循环的循环条件为false的时候,才会完全跳出内层循环,才可以结束外层循环的当次循环
-
其中的循环结构,使用for、while和do-while都可以当作循环的语块
实例
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++) {
System.out.println("i = "+i+" j = "+j);
}
}
}
}
运行结果
i = 0 j = 0
i = 0 j = 1
i = 0 j = 2
i = 1 j = 0
i = 1 j = 1
i = 1 j = 2
i = 2 j = 0
i = 2 j = 1
i = 2 j = 2
i = 3 j = 0
i = 3 j = 1
i = 3 j = 2
i = 4 j = 0
i = 4 j = 1
i = 4 j = 2
Process finished with exit code 0
循环控制结构
break
作用:跳出循环,不再执行跳出的循环
例子
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
//当i循环到3的时候就跳出循环,之后不再进行这次循环
if (i == 3) {
break;
}
System.out.println("i = " + i);
}
}
}
i = 0
i = 1
i = 2
Process finished with exit code 0
continue
作用:跳出当次循环,开始下一个循环周期
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++) {
//当i==1的时候,直接就结束整个方法了,后面的循环已经无法执行了
if(i == 1){
return;
}
System.out.println("i = " + i);
System.out.println("j = " + j);
}
}
}
}
i = 0
j = 0
i = 0
j = 1
i = 0
j = 2
Process finished with exit code 0
例子
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
//当i循环到3的时候就跳出当次循环,之后的循环继续
if (i == 3) {
continue;
}
System.out.println("i = " + i);
}
}
}
i = 0
i = 1
i = 2
i = 4
Process finished with exit code 0
return
直接结束整个函数,在一个函数中,return后面的代码无法得到执行
例子
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++) {
//当i==1的时候,直接就结束整个方法了,后面的循环已经无法执行了
if(i == 1){
return;
}
System.out.println("i = " + i);
System.out.println("j = " + j);
}
}
}
}
注意事项
- break只能用于switch语句和循环语句中。
- continue 只能用于循环语句中。
- 二者功能类似,但continue是终止本次循环, break是终止本层循环。
- break、 continue之后不能有其他的语句,因为程序永远不会执行其后的语句。
- 很多语言都有goto语句, goto语句可以随意将控制转移到程序中的任意一条语句上,然后执行它。但使程序容易出错。 Java中的break和continue是不同于goto的
标签语法
作用:标识循环代码块(for,while,do-while),配合continue和break精确控制循环,一般多重循环使用较多
- 不需要进行二次判断就可以进行循环控制,非常方便
- 标签语句必须紧接在循环的头部。标签语句不能用在非循环语句的前面。
示例
普通嵌套循环
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
//普通嵌套循环
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
System.out.println("i = " + i);
System.out.println("j = " + j);
}
}
}
}
i = 0
j = 0
i = 0
j = 1
i = 1
j = 0
i = 1
j = 1
i = 2
j = 0
i = 2
j = 1
Process finished with exit code 0
break使用标签
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
//外层标签
outer:
for (int i = 0; i < 3; i++) {
//内部标签
inner:
for (int j = 0; j < 2; j++) {
//当i==1的时候直接跳出外层循环
if(i == 1){
//跳出外层循环
break outer;
}
System.out.println("i = " + i);
System.out.println("j = " + j);
}
}
}
}
i = 0
j = 0
i = 0
j = 1
Process finished with exit code 0
continue使用标签
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
//外层标签
outer:
for (int i = 0; i < 3; i++) {
//内部标签
inner:
for (int j = 0; j < 2; j++) {
//当i==1的时候直接跳过本次外层循环
if(i == 1){
//跳过本次外层循环
continue outer;
}
System.out.println("i = " + i);
System.out.println("j = " + j);
}
}
}
}
i = 0
j = 0
i = 0
j = 1
i = 2
j = 0
i = 2
j = 1
Process finished with exit code 0
数组
定义:是多个相同类型数据按一定顺序排列的集合, 并使用一个名字命名, 并通过编号的方式对这些数据进行统一管理。
- 数组元素类型是唯一的,可以是基本类型,也可以是引用类型
- 数组一旦初始化,其所占的空间将会固定下来,即使是把数组元素的数据清空,但是所占的内存空间也不会清除
- 数组一旦初始化,数组的长度是不可变的
定义数组
格式
type[] arrayName
type arrayName[]
注意事项
- 两种方式都可以定义数组,但是主要推荐第一个方式,原因见下
type[] arrayName
PKtype arrayName[]
两种变量定义方式都可以定义变量,但是主要推荐使用前者,具体原因如下:
数组其实也是一种数据类型,例如int,它的数组类型不正对应int []吗?这样看起来就是一一对应的,可读性很高,而第二种风格是c语言的定义变量的风格,所以会导致我们很喜欢用第二种方式去定义数组(大部分人最开始学习编程语言都是c语言),但是实际上这样定义数组却可读性很差,括号在标识符后面,这样很难理解其中的意思。
- 这个只是定义了数组,还没有初始化是不能使用的
- 定义的时候仅仅定义了一个引用变量(定义了一个指针,此时还没有指向任何有效内存)
- int [](其他类型也是一样的)实际上是一种引用类型,创建的是int [](其他类型也是一样的)类型的对象
初始化数组
定义:为数组的数组元素分配内存空间,并为每个数组元素赋值
分类
静态初始化:初始化时显示指定每个元素的初始值,由系统决定数组长度
动态初始化:只需要定义数组长度,由系统为数组元素分配初始值
格式
静态初始化:
arrayName = new type[]{element1,element2,element3,element4g};
动态初始化:
arrayName = new type [length];
-
必须指定length参数,length参数为数组的长度
-
type必须和之前定义数组的的type类型一样或者是定义数组时使用type类的子类
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
//定义Object数组,为其静态赋值String类型的字符串
Object[] oj = new String[]{"陪伴是","最长情","的告白"};
//循环输出每个变量的内容
for(Object s : oj){
System.out.println("s = " + s);
}
}
}
s = 陪伴是
s = 最长情
s = 的告白
Process finished with exit code 0
- 动态初始化的时候每个类型都有默认的初始值,如下图:
- 一般使用定义初始化一步到位的模式:
int [] prices = new int [5];
int [] prices = new int[]{1,2,23,4,5};
使用数组
一般使用数组名称加一个方括号([])来访问数组,方括号里面包含一个整型数值,那个数值就是数组下标索引(从0开始),这样使用数组的某个元素就相当于使用某个变量了
实例
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
//定义一个数组
int[] a = new int[]{1,2,3,4,5,6,7,8};
//a[1]的值等于2,因为数组的下标是从0开始的
System.out.println("a[1] = " + a[1]);
}
}
-
记住常见的数组使用错误
- java.lang.ArrayIndexOutOfBoundsException: N(数组访问下标越界,这个N就是你试图访问越界的下标值)
-
每个数组都有默认的length属性,可以返回数组的长度
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
//定义一个数组
int[] a = new int[]{1,2,3,4,5,6,7,8};
//输出a数组的长度为8
System.out.println(a.length);
}
}
即可以循环访问数组元素(循环访问数组的方法之一)
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
//定义一个数组
int[] a = new int[]{1,2,3,4,5,6,7,8};
//循环访问元素,输出每个元素的值
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
}
}
foreach循环
定义:Java5之后提供的一种更简单的循环,可用于遍历数组和集合(在后面),优点的无需下标和长度就可以自动遍历数组和集合的每个元素
语法格式
for(type variableName : array){
//这里就可以自动迭代访问array里面的每个元素了
}
- variableName是一个形参名,foreach循环自动将数组元素赋值给该变量
- 这个只能遍历数组,不能修改数组元素的值,variableName只是一个临时变量,修改没有任何意义
使用实例
/**
* @author wangxin
* @create 2020-07-24 9:08
*/
public class Main {
public static void main(String[] args) {
//定义一个数组
int[] a = new int[]{1,2,3,4,5,6,7,8};
//循环访问元素,输出每个元素的值
for (int temp : a) {
System.out.println(temp);
}
}
}
堆栈存储数组内存分析
Java中具有堆(Heap)内存和栈(Stack)内存,他们都是拿来存储变量的,但是存储的方式不一样,具体见如下:
- 首先两个概念,堆内存和栈内存,如果不清楚可以这样理解,两个都是底层用于存放变量的的“容器”,它们如图所示:
-
栈(Stack):
- 用于存储基本类型变量(char,int,long)、引用类型变量(切记只是存储它的“指针”,并不是真正的实体数据)和方法函数
- 存储的基本类型变量和对象的引用变量都是在栈内存中分配
- 栈的优点是:存取速度比堆要快,仅次于寄存器,栈数据可以共享。
- 栈的缺点是:存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。
- 当超过变量的作用域后,Java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。
-
堆(Heap):
- 用于存储实例对象(new出来的对象)和数组的实体数据
- 在堆中分配的内存,由java虚拟机自动垃圾回收器来管理。
- 栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,在栈中的这个特殊的变量就变成了数组或者对象的引用变量,引用变量相当于为数组或者对象起的一个别名,或者代号。
- 引用变量是普通变量,定义时在栈中分配内存,极其类似于C语言指针
-
一旦数组定义后,数组在堆内存中是不可以改变的!!
-
一维数组的内存图如下:
多维数组
Java从实质上讲,没有多维数组,多维数组只是引用多个数据
图解
- 这是一个典型的二维数组,一个指针指向多个堆内存,这样就可以表示一个二维数组了