目录
计算机在任何情况下都只能识别二进制。简单点,只认识 0 和 1
标识符
-
什么是标识符?
在Java源程序中,可以由程序员自己命名的“单词”都是标识符。
-
标识符的命名规则?
规则:如果不按照这个规则来命名的话,编译器会报错,这属于语法。
- 只能有“数字(0~9)、字母、下划线(_)、美元符号($)”组成,不能含有其他的符号
- 不能以数字开头、严格区分大小写、关键字不能做标识符
- 理论上无长度限制,但是最好不要太长
-
标识符的命名规范?
规范:属于一种默认遵守的约定,不按照规范来,编译也不会报错
- 最好是见名知意
public class UserService{
public void login(String username, String password){
}
}
- 遵循驼峰命名方式
UserServer
MyUser
- 类名、接口名:首字母大写,后面的每一个单词首字母大写
- 变量名、方法名:首字母小写,后面每一个单词首字母大写。
- 常量:每一个字母都大写
-
标识符可以标记什么元素?
类名、方法名、变量名、接口名、常量名。。。。。。
public class MyFirstTest{ //MyFirstTest是一个类名,名字可以修改
//main是一个方法名
public static void main(String[] args){ //args是一个变量名
}
//doSome就是方法名
public static void doSome(){
int i = 20;//i就是变量名
}
}
关键字
class | extends | implements | interface | import |
do | if | else | for | return |
package | break | case | continue | default |
switch | while | false | true | null |
boolean | byte | char | short | int |
long | float | double | try | catch |
throw | throws | finally | abstract | fianl |
native | private | protected | public | static |
synchronized | transient | volatile | instanceof | new |
super | void | assert | enum | goto |
const |
字面值
-
什么是字面值?字面值包括什么?
字面值就是数据。
- 10、100 属于整数型字面值
- 3.14 属于浮点型字面值
- “abc”、"大家好" 属于字符串型字面值
- true、false 属于布尔型字面值
- 'A'、'人' 属于字符型字面值
-
字面值做什么的?
字面值是Java源程序的组成部分之一,标志符合关键字,它们都是java源程序的组成部分
-
使用字面值注意什么?
- java语言当中所有的字符串型字面值必须使用双引号括起来,双引号是半角
- java语言当中所有的字符型字面值必须使用单引号括起来,单引号是半角
public class ConstTest{
public static void main(String[] args){
System.out.println("abcd");
System.out.println("你好");
//10是一个字面值,是一个整数型数值,那么既然是数据存储在内存当中,必会占用一定的内存空间。
System.out.println(100);
System.out.println(3.14);
System.out.println("3.1");
System.out.println('A');
System.out.println(true);
//编译报错,因为单引号中只能存放单个字符,属于字符型字面值
//System.out.println('ABC');
}
}
变量
-
什么是变量?变量的要求?
- 变量本质上来说是内存中的一块空间。
- 这块空间 --> 变量包括三部分:数据类型、名称、字面值【数据】
- 变量是内存中存储数据的最基本的单元。
- 变量中存储的具体的"数据"必须和变量的"数据类型"一致,当不一致的时候编译报错。
-
数据类型的作用?
- 不同的数据有不同的类型,不同的数据类型底层会分配不同的大小的空间。
- 数据类型是指导程序在运行阶段应该分配多大的内存空间。
-
声明/定义变量的语法格式?
数据类型 变量名;
int i;
double num;
-
变量的声明和赋值
- 变量在一行上可以声明多个
int a,b,c;
数据类型 变量名 = 字面值;
int i;
i = 2;
- 要求:字面值的数据类型必须和变量的数据类型一致。
= 等号是一个运算符,也叫赋值运算符,赋值运算符先运算等号右边的表达式,表达式执行结束之后的结果赋值给左边变量。
- 声明和赋值可以放在一起完成
int i = 2;
- 变量赋值之后,可以重新赋值,变量的值可变化
int i = 10;
System.out.println(i); //10
i = 20;
System.out.println(i); //20
- java中的变量必须先声明,再赋值,才能访问。
public class Test{
public static void main(String[] args){
int i; //声明一个int类型的变量,起名i
//System.out.println(i); //编译报错:变量i并未初始化
}
}
int i; 程序执行到这里,内存空间并没有开辟出来,变量i并没有初始化。所以没有赋值之前是无法访问的。
-
有了变量的概念之后,内存空间得到了重复度额使用
-
访问变量包括两种访问形式
- 第一种:读取变量中保存的具体的数据 --> get/获取
- 第二种:修改变量中保存的具体的数据 --> set/设置
i = 20; //set
System.out.println(i); //get
-
变量的作用域
- 变量的作用域,其实描述的就是变量的有效范围。
在什么范围之内是可以被访问的,只是出了这个范围该变量就无法访问了。
public class Test{
static int k = 90; //注意:在这里的static不能去掉
public static void main(String[] args){
//变量i的作用域是main方法
//在整个main方法当中是有效的,可见的,可以访问的
int i = 100;
System.out.println(i); //可以
System.out.println(k); //可以
//以下会编写一个for循环语句
for(int a = 0; a < 10; a++){ //a变量的作用域是整个for循环,for循环结束之后,a变量的内存就释放了
}
//System.out.println(a); //这里无法访问a变量
int j; //作用域是main方法
for(j = 0; j < 10; j++){
}
System.out.println(j); //访问的是main方法中的j变量
}
public static void doSome(){
//System.out.println(i); //这里无法访问main方法当中的变量i,已经出了i变量的作用域。
System.out.println(k); //可以访问
}
}
总之,记住一句话:出了大括号就没关系了
-
代码执行顺序
- 在方法体当中的java代码,是遵守自上而下的顺序一次执行的,逐行执行。
第一行代码;
第二行代码;
第三行;
特点:第二行的代码必须完整的结束之后,第三行程序才能执行。
- 在同一个"作用域"当中,变量名不能重名,但是变量可以重复赋值。
public void test(){
int i = 100;
System.out.println(i); // 100
//int i = 30; // 会报错,变量名不能重名
i = 200; // 变量可以重复赋值。
System.out.println(i); // 200
}
-
变量的分类
根据变量声明的位置来分类
- 局部变量 : 在方法体当中声明的变量叫做局部变量
- 成员变量 : 在方法体外【类体之内】声明的变量叫做成员变量
在不同的作用域中,变量名是可以相同的。在同一个作用域中,变量名不能重名。
public class Test{
int k = 200; //成员变量
//主方法:入口
public static void main(String[] args){
int i = 10; //i变量就是局部变量
System.out.println(i); //java遵循"就近原则"
}
int i = 100; //声明 成员变量
//类体中不能直接编写java语句【除声明变量之外】
//System.out.println(i);
//doSome方法
public static void doSome(){
int i = 90; //局部变量
}
}
数据类型
- 数据类型的作用是什么?
- 程序当中有很多数据,每个数据都是有相关类型的,不同数据类型的数据占用空间大小不同。数据类型的作用是指导JVM在运行程序的时候给该数据分配多大的的内存空间。
- java中的数据类型包括两种:基本数据类型、引用数据类型
- 基本数据类型包括四大类(八小类):
第一类:整数型 --> byte, short, int, long
第二类:浮点型 --> float, double
第三类:布尔型 --> boolean
第四类:字符型 --> char
- 引用数据类型:
例如:接口、类、数组等等
- 注意:字符串"abc"不属于基本数据类型,属于"引用数据类型",字符属于基本数据类型
- 字符串使用双引号 "abc"
- 字符使用单引号 'a'
- 各种数据类型的信息
-
计算机在任何情况下都只能识别二进制。简单点,只认识 0 和 1
- 什么是二进制?
- 数据的一种表示形式。十进制表示满十进一的原则。二进制表示满二进一原则。
- 字节(byte)
1 Byte = 8 bit 【1个字节 = 8个比特位】 1个比特位表示一个二进制位:1/0
1 KB = 1024 Byte
1 MB = 1024 KB
1 GB = 1024 MB
1 TB = 1024 GB
- 字符编码
为了让计算机可以表示现实世界当中的文字,我们需要进行人为的干涉,需要人负责提前制定好"文字"和"二进制"之间的对照关系。这种对照转换关系被称为:字符编码。
计算机最初只支持英文,最先出现的字符编码是:ASCII码
'a' --> 97【01100001】
'A' --> 65
'0' --> 48
'a' --(按照ASCII解码)--> 01100001
01100001 --(按照ASCII编码)--> 'a'
编码和解码的时候采用同一套字典/对照表,不会出现乱码。
当解码和编码的时候采用的不是同一套对照表,会出现乱码问题。
随着计算机的发展,后来出现了很多种编码方式,如:ISO-8859-1(latin-1)、GB2312、GBK、 GB18030、big5、UTF-8、UTF-16、UTF-32等等
现在在实际开发中,一般使用UTF-8编码方式较多。【统一编码方式】
- 整数型
- byte, short, int, long
- java语言当中的"整数型字面值"被默认当做int类型来处理。要让这个"整数型字面值"被当做long类型来处理的话,需要在"整数型字面值"后面加上l/L【建议使用大写的L】。
- java语言当中的整数型字面值有三种表达方式:
第一种方式:十进制【是一种缺省默认值的方式】
第二种方式:八进制【在编写八进制整数型字面值的时候需要以0开始】
第三种方式:十六进制【在编写十六进制整数型字面值的时候需要以0x开始】
int a = 10;
int b = 010; //整数型字面值以0开头的,后面那一串数字就是八进制形式
int c = 0x10; //整数型字面值以0x开头的,后面那一串数字就是十六进制形式
System.out.println(a); //10
System.out.println(b); //8
System.out.println(c); //16
System.out.println(a + b + c); //34
- 当一个整数字面值没有超出byte, short, char的取值范围,这个字面值可以直接赋值给byte, short, char类型的变量。这种机制SUN允许了,目的是为了方便程序员的编程。
//这说明:在java语言当中,当一个整数型字面值没有超过byte, short, char类型取值范围的话,
//该字面值可以直接赋值给byte, short, char类型的变量。
byte a1 = 50; //可以
byte b1 = 127; //可以
char cc = 65535; // 可以
short s = 32767; // 可以
-
补码
(一)补码: 整型以补码形式存放
(1)一个正数的补码是此数的二进制形式
(2)一个负数的补码,先求其绝对值的补码,然后该补码所有位取反(0、1交换),所得之数最后加 1
(3)0 的补码, 所有位全是 0
注: 在存放整型的存储单元中最左边一位是用来表示符号的。(正整数最左边一位是 0, 负整数最左边一位是 1)
8位二进制(一个字节)补码所代表的整数:
二进制 十进制
0000 0000 ------------------------- 0
0000 0001 ------------------------- 1
...... .....
0000 1111 ------------------------- 127 == 2^(8-1)-1 最大正数
1000 0000 ------------------------- -128 == -2^(8-1) 最小负数
1000 0001 ------------------------- -127 == -(2^(8-1)-1)
1111 1111 ------------------------- -1
总结:
二进制补码 n 位所代表的整数范围: -2^(n-1) ~ 2^(n-1)-1
//原始数据:00000000 00000000 00000000 00000000 10000000 00000000 00000000 00000000
//强转后的数据:10000000 00000000 00000000 00000000
//10000000 00000000 00000000 00000000目前存储在计算机内部,计算机存储数据都是采用补码的形式存储的。所以10000000 00000000 00000000 00000000现在是一个补码形式
//将以上的补码转换到原码就是最终的结果
long k = 2147483648L;
int e = (int)k;
System.out.println(e); //损失精度严重,结果是负数【-2147483648】
//原始数据:00000000 00000000 00000000 11000110
//强制类型转换之后:11000110
//11000110现在在计算机当中存储,它是一个补码,将补码转换成原码就是该数字:
//11000110 - 1 --> 11000101
//00111010 【2 + 8 + 16 + 32】 --> 58
//-58
byte q = (byte)198;
System.out.println(q); //-58
计算机二进制有三种表示形式:
原码、反码、补码
计算机在任何情况下底层表示和存储数据的时候采用了补码形式。
正数的补码:和原码相同
负数的补码:负数的绝对值对应的二进制所有二进制位取反,再加1
补码:10000000
原码计算过程:
* 10000000 - 1 --> 01111111
* 10000000 --> 128
* -128
//编译报错,128这个int类型的字面值已经超过byte类型的取值范围,不能直接赋值给byte类型的变量
//byte c1 = 128;
//纠正错误,需要使用强制类型转换符,但是一定会损失精度
//原始数据:00000000 00000000 00000000 10000000
//强转后的数据:10000000【这是存储在计算机内部的,这是一个补码】
byte c1 = (byte)128;
System.out.println(c1); //-128
- 浮点型
- float 单精度【4个字节】
- double 双精度【8个字节,精度较高】
//3.0是double类型的字面值,d是double类型的变量
//不存在类型的转换
double d = 3.0;
System.out.println(d);
double的精度太低【相对来说的】,不适合做财务软件。财务涉及到钱的问题,要求精度较高,所以SUN在基本SE类库当中为程序员准备了精度更高的类型,只不过这种类型是一种引用数据类型,不属于基本数据类型,它是:java.math.BigDecimal
- 在java语言当中,所有的浮点型字面值【如:3.0】,默认被当作double类型来处理,要想该字面值当做float类型来处理,需要在字面值后面添加F/f
//5.1是double类型的字面值,f是float类型的变量
//大容量转换到小容量需要加强制类型转换符,所以以下程序编译错误。
//float f = 5.1; // 编译报错
//解决方案
//float f = (float)5.1; //第一种方式
float f = 5.1F; //第二种方式
- 注意:
double和float在计算内部二进制存储的时候存储的都是近似值。在现实世界当中有一些数字是无限循环的,例如:3.333333333....
计算机的资源是有限的,用有限的资源存储无限的数据只能存储近似值。
- 字符类型
- 普通的字符
//普通字符
char c = 'a'; //定义一个char类型的变量,起名c,同时赋值字符'a'
System.out.println(c);
//一个中文占用2个字节,char类型正好是2个字节,所以java中的char类型变量可以存储一个中文字符
char x = '国';
System.out.println(x);
//ab是字符串不能使用单引号括起来
//char y = 'ab'; //编译错误
//"a"是字符串类型,k变量是char类型
//char k = "a"; //类型不兼容,编译出错
- 转义字符 --> \(反斜杠)
转义字符出现在特殊字符之前,会将特殊字符转换成普通字符。
\n --> 换行符
\t --> 制表符
\' --> 普通的单引号
\\ --> 普通的反斜杠
\" --> 普通的双引号
'\u4e2d' --> '中' 【反斜杠 + Unicode编码】
//普通的n字符
char c1 = 'n';
System.out.println(c1);
//编译成功,说明以下不是一个字符串,而是"一个"字符
//反斜杠在java语言当中具有转移功能
char c2 = '\n'; //这是一个"换行符",属于char类型的数据
System.out.println(c2);
char n = '\u4e2d'; //'中'对应的Unicode编码是 4e2d
System.out.println(n);
- char类型默认值
char q = '\u0000'; //char类型的默认值
System.out.println(q); //输出: 空
System.out.println(q + 1); //输出:1
- 布尔类型
- 在java语言当中Boolean类型只有两个值:true、false,没有其它值。不像C语言当中,0和1可以表示假和真。在底层存储的时候,Boolean类型占用1个字节,因为实际存储的时候false底层是0,true底层是1。布尔类型在实际开发当中非常重要,经常使用在逻辑运算和条件控制语句当中。
//boolean flag = 1; //编译错误:不兼容的类型
//boolean loginSuccess = false;
boolean loginSuccess = true;
//if语句
if(loginSuccess){
System.out.println("登陆成功!");
}else{
System.out.println("对不起,登录失败!");
}
-
数据类型之间的相互转换
* 八种基本数据类型当中除布尔类型之外剩下的7种类型之间都可以互相转换
* 小容量向大容量转换,称为自动类型转换,容量从小到大排序:
byte < short(char) < int < long < float < double
注意:
任何浮点类型不管占用多少个字节,都比整数型容量大。
char和short可表示的种类数量相同,但是char可以取更大的正整数。
* 大容量转换成小容量,叫做强制类型转换,需要加强制类型转换符,程序才能编译通过,但是在运行阶段可能会损失精度,所以谨慎使用。
* 当整数字面值没有超出byte, short, char的取值范围,可以直接赋值给byte, short, char类型的变量。
* byte, short, char混合运算的时候,各自先转换成int类型再做运算。
* 多种数据类型混合运算,先转换成容量最大的那种类型再做运算。
* 默认就是int类型
byte b = 3; //可以编译通过,3没有超过byte类型取值范围
int i = 10;
byte b = i / 3; //编译报错,编译器只检测语法,不会"运算" i / 3
- 不能超过数据类型的范围,否则编译会报错
//byte a = 1000; //编译出错,1000超出了byte的范围
byte a = 20; //正确,因为20没有超过byte的范围,所以赋值
short b = 1000; //正确,因为数值1000没有超过short类型的范围,所以赋值
- 小容量的类型 -(转换)-> 大容量的类型
int c = 1000;
long d = c; //可以,自动转换
- 大容量的类型 -(转换)-> 小容量的类型(容易出现精度丢失问题)
long d = 1000;
//int e = d; //编译错误,出现精度丢失问题,大容量的类型-->小容量的类型会出现的问题
//将long类型强制转换成int类型
int e = (int)d; //因为值1000,没有超出int范围,所以转换是正确的
- java中的运算会转换成最大类型
//因为java中的运算会转换成最大类型
//而10和3默认为int,所以运算后的最大类型也是int
//所以是正确的
int f = 10/3;
long g = 10; //声明10为long类型
//出现错误,多个数值在运算过程中,会转换成容量最大的类型
//以下实例最大的类型为long,而h为int,所以就会出现大类型(long)到小类型(int)的转换,将会出现精度丢失问题。
//int h = g/3; //编译错误
//可以强制转换,因为运算结果没有超过int范围
//int h = (int)g/3; //可以
//可以采用long类型来接收运算结果
//long h = g/3; //可以
- 出现精度损失问题,也有可能是优先级的问题
long g = 10;
//将g转换成int,然后又将int类型的g转换成byte,最后byte类型的g和3运算,
//那么它的运算结果类型就是int,所以int赋值给byte就出现精度损失问题
//byte h = (byte)(int)g/3; //错误
//byte h = (byte)(int)(g/3); //可以
//不能转换,因为优先级的问题
//byte h = (byte)g/3; //编译错误
//可以转换,因为运算结果没有超出byte范围
//byte h = (byte)(g/3); //可以
//可以转换,因为运算结果没有超出short范围
//short h = (short)(g/3); //可以
- 关于数据类型的运算
short i = 10;
byte j = 5;
//错误,short和byte运算,首先会转换成int再运算,所以运算结果为int,int赋值给short就会出现精度丢失问题
//short k = i + j; //编译错误
//可以将运算结果强制转换为short类型
//short k = (short)(i + j); //可以
//因为运算结果为int,所以可以采用int类型接收
int k = i + j;
char l = 'a';
System.out.println( (byte)l ); //输出:97,也就是a的ASCII值
int m = l + 100;
System.out.println(m); //输出:197,取得a的ASCII码值,然后与100进行相加运算
double dd = 10 / 3;
System.out.println(dd); //输出:3.0
dd = 10.0 / 3;
System.out.println(dd); //输出:3.3333333333333335
运算符
- 算术运算符
- 一个表达式当中有多个运算符,运算符有优先级,不确定的加小括号,优先级得到提升。【没有必要去专门记忆运算符的优先级。】
+ 求和
- 相减
* 乘积
/ 商
% 取余数【取模】
int i = 10;
int j = 3;
System.out.println( i + j ); //13
System.out.println( i - j ); //7
System.out.println( i * j ); //30
System.out.println( i / j ); //3
System.out.println( i % j ); //1
++ 自加1 【单目运算符】
-- 自减1 【单目运算符】
++运算符可以出现在变量前,也可以出现在变量后,无论是变量前还是变量后只要++运算符结束,该变量中的值一定会自加1
//++ 出现在变量后,规则:先做赋值运算,再对变量中保存的值进行自加1。
int a = 100;
int b = a++;
System.out.println(a); //101
System.out.println(b); //100
//++ 出现在变量前,规则:先进行自加1运算。然后再进行赋值操作。
int m = 20;
int n = ++m;
System.out.println(m); //21
System.out.println(n); //21
int e = 100;
System.out.println( e++ ); //100
System.out.println( e ); //101
int s = 100;
System.out.println( ++s ); //101
System.out.println( s ); //101
- 关系运算符
> 大于
>= 大于等于
< 小于
<= 小于等于
== 等于 【=是赋值运算符】
!= 不等于
【关系运算符的运算结果一定是布尔类型:true/false】
关系运算符的运算运算原理:
int a = 10;
int b = 10;
a > b 比较的时候,比较的是a保存的10这个值和b保存的10这个值之间的大小比较。
a == b 也是如此。
int a = 10;
int b = 20;
System.out.println(a > b); //true
System.out.println(a >= b); //true
System.out.println(a < b); //false
System.out.println(a <= b); //false
System.out.println(a == b); //false
- 逻辑运算符(也叫 布尔运算符)
& 逻辑与【并且】(两边的算子都是true,结果才是true)
| 逻辑或【或者】(两边的算子只要是一个是true,结果就是true)
System.out.println( 5 > 3 & 5 > 2 ); //true
System.out.println( 5 > 3 & 5 > 7 ); //false
System.out.println( 5 > 3 | 5 > 7 ); //true
! 逻辑非(取反,!false就是true,!true就是false,这是一个单目运算符)
System.out.println( !true ); //false
System.out.println( !false ); //true
^ 逻辑异或(两边的算子只要不一样,结果就是true)
System.out.println(true ^ false); //true
System.out.println(false ^ false); //false
System.out.println(true ^ true); //false
&& 短路与
|| 短路或
* 逻辑运算符要求两边的算子都是布尔类型,并且逻辑运算符最终的运算结果也是一个布尔类型。
* 短路与 和 逻辑与最终的运算结果是相同的,只不过短路与存在短路现象。( 短语或 和 逻辑与 同理)
* 什么情况下发生短路现象呢?
- 发生短路或:第一个表达式执行结果是true,会发生短路或。
- 发生短路与:第一个表达式执行结果是false,会发生短路与。
//逻辑与
int x = 10;
int y = 8;
System.out.println( x < y & ++x < y);
System.out.println(x); //11
//短路与
int m = 10;
int n = 8;
//m < n 结果是false,整个表达式结果已经确定是false,所以后面的表达式没有执行,这种现象被称为短路现象。
//短路与才会有短路现象,逻辑与是不会存在短路现象的。
System.out.println( m < n && ++m < n);
System.out.println(m); //10
* 什么时候选择使用逻辑与运算符?什么时候选择使用短路与运算符?
从某个角度来看,短路与更智能。由于后面的表达式可能不执行,所以执行效率较高。这种方式在实际的开发使用较多。短路与比逻辑与使用的多,短路与更常用。但是,在某些特殊的业务逻辑当中,要求运算符两边的算子必须全部执行,此时必须使用逻辑与,不能使用短路与,使用短路与可能导致右边的表达式不执行。
- Java中的 "+" 运算符
+运算符在java语言当中有两个作用:
- 加法运算,求和
- 字符串的连接运算
* 当"+"运算符两边的数据都是数字的话,一定是进行加法运算。
* 当"+"运算符两边的数据只要有一个数据时字符串,一定会进行字符串连接运算,
并且,连接运算之后的结果还是一个字符串类型。
简单来说,
数字 + 数字 ---> 数字【求和】
数字 + "字符串" ---> "字符串"【字符串连接】
* 在一个表达式当中可以出现多个"+",在没有添加小括号的前提下,遵循自左向右的顺序一次运算。
System.out.println(10 + 20 + 30); //这里的加号也是求和
System.out.println(10 + 20 + "30"); //自左向右的顺序一次运算,第一个加号是求和,第二个是字符串连接
System.out.println(10 + (20 + "30")); //102030
- 赋值运算符
赋值运算符包括两种:基本的赋值类运算符(=)、扩展的赋值运算符(+=, -+, /=, %=)
- 基本赋值运算符:
int i = 10;
System.out.println(i); //10
i = i + 5;
System.out.println(i); //15
赋值类的运算符优先级:先执行等号右边的表达式,将执行结果赋值给左边的变量。
以 += 为例,其他的扩展的赋值运算符类似。
byte i = 10;
i += 5; //等同于: i = (byte)(i + 5);
i += 128; //可以,等同于:z = (byte)(z + 128);
System.out.println(i); //【损失精度】
int k = 10;
k += 5; //等同于:k = (int)(k + 5);
long x = 10L;
int y = 20;
y += x; //等同于:y = (int)(y + x);
扩展类的赋值运算符不改变运算结果类型,假设最初这个变量的类型是byte类型,无论怎么样进行追加或追减,最终该变量的数据类型还是byte类型。【有可能会造成精度丢失】
注意:编译器只检测语法,不运行程序
byte b = 10; //10没有超过byte取值范围,可以直接赋值
//b = 15; //编译通过,15没有超过byte取值范围
//编译错误,编译器只检测语法,不运行程序,编译器发现b+5的类型是int类型,b变量的数据类型是byte,大容量向小容量转换需要加强制类型转换符,所以以下程序编译报错。
//b = b + 5; //错误
//纠正错误
b = (byte)(b + 5); //可以
System.out.println(b); //15
下面这种使用 += 运算符就可以编译成功
byte x = 10;
x += 5; //可以,该句等同于:x = (byte)(x + 5)
System.out.println(x); //15
- 三目运算符【也叫三元运算符,条件运算符】
* 语法规则:
布尔表达式 ? 表达式1 : 表达式2
* 三目运算符的执行原理?
当布尔表达式的结果是 true 的时候,选择 表达式1 作为整个表达式的执行结果
当布尔表达式的结果是 false 的时候,选择 表达式2 作为整个表达式的执行结果
boolean sex = false; //布尔类型的变量
//sex ? '男' : '女'; //编译报错,因为它不是一个完整的java语句
char c = sex ? '男' : '女';
System.out.println(c); //女
- 位运算符
>> <<
位运算符是对二进制位进行操作的,效率上会比其他运算符要快一些。
>> 1 二进制右移1位。
<< 2 二进制右移2位。
// 10的二进制位是: 0000 1010 【10】
// 10的二进制右移1位是:0000 0101 【5】
System.out.println(10 >> 1); //5
// 10的二进制位是:0000 1010 【10】
// 10的二进制右移1位是:0001 0100 【20】
System.out.println(10 << 1); // 20
简单来说,就是 右移1位就是除以2,左移1位就是乘以2
控制语句
-
if 语句 --> 选择结构
if 语句,属于选择结构,if语句又被称为分支语句/条件控制语句。
if语句的语法结构:有以下四种编写方式
- 第一种:
if(布尔表达式){
java语句;
........
}
- 第二种:
if(布尔表达式){
java语句;
.....}else if(布尔表达式){
java语句;
.....
}else if(布尔表达式){
java语句;
java语句;
....
}.....
- 第三种:【注意:以下编写方式都带有else分支,这两种方式可以100%保证会有分支执行】
if(布尔表达式){
java语句;
.....
}else{
java语句;
....
}
- 第四种:
if(布尔表达式){
java语句;
.....
}else if(布尔表达式){
java语句;
.....
}else if(布尔表达式){
java语句;
....
}else{
java语句;
.....
}
【重点】:对于java中的 if 语句来说,只要有一个分支执行,整个if语句全部结束。
- "所有的控制语句"都是可以嵌套使用的,只要合理嵌套就行。还是要注意代码的编写格式的,该缩进缩进,该对齐对齐,看上去也很舒服。
if(){
if(){
}
}else{
if(){
}else{
}
}
- if语句的分支中只有一条java语句的话,大括号可以省略不写。不过这样不推荐使用。
if(true/false)
一条java语句;
- 还有可能出现错误
boolean sex = false;
if(sex)
System.out.println("男");
//System.out.println("嘿嘿"); //没有花括号,只能控制一条java语句
else //去掉上句注释,此处出现编译错误
System.out.println("女");
- 还是希望像下面代码一样,带上花括号的,这样也方便以后的代码修改
double num= 1.0;
//double num = 6.0;
//判断语句
if(num < 5){
System.out.println("大于5!");
}
- 用 if 语句做一个简单的程序
键盘上获取数据Scanner
在这之前,先解释以下怎么从键盘上获取数据Scanner
System.out.println(); 负责向控制台输出【从内存到控制台,输出的过程,这是从内存中出来】
接收用户键盘输入,从"键盘"到"内存"。【输入的过程,到内存中去】
//第一步:创建键盘扫描器对象
Scanner s = new Scanner(System.in);
//第二步:调用Scanner对象的next()方法开始接收用户键盘输入,程序执行到这里会停下来,等待用户的输入,当用户输入的时候,并且最终敲回车键的时候,键盘的信息会自动赋值给userInputContent,程序执行到这里,用户输入的信息已经到内存中了。
//String userInputContent = s.next(); //接收文本【以字符串的形式接收】
int num = s.nextInt(); //接收数字【以整数型int的形式来接收】
System.out.println("您输入的数字是:" + num);
需要导入 java.util.Scanner 这个包,如果没导入这个包可以使用全限定名称,创建键盘扫描器对象
//第一步:创建键盘扫描器对象
java.util.Scanner s = new java.util.Scanner(System.in); //全限定名称
假设给定一个热的年龄(这个年龄需要从键盘输入),根据年龄来判断这个人出入生命的哪个阶段,年龄必须在[0-150]
import java.util.Scanner;
public class IfTest{
public static void main(String[] args){
//接收键盘输入:年龄【输入的时候必须输入数字】
Scanner s = new Scanner(System.in);
System.out.print("请输入您的年龄:"); //输出提示信息
int age = s.nextInt(); //停下来等待用户输入
String str = "老年";
if(age < 0 || age > 150){
str = "输入年龄不合法!";
}else if(age <= 5){
str = "幼儿";
}else if(age <= 10){
str = "少年";
}else if(age <= 18){
str = "青少年";
}else if(age <= 35){
str = "青年";
}else if(age <= 55){
str = "中年";
}
System.out.println(str);
}
}
-
switch --> 选择结构
* switch语句也属于选择结构,也会是分支语句
* switch语句的语法结构:
switch(int或String类型的字面值或变量){
case int或String类型的字面值或变量 :
java语句;
.....
break;
case int或String类型的字面值或变量 :
java语句;
java语句;
.....
break;
.....
default :
java语句;
.....
}
* switch语句的执行原理:
switch后面小括号当中的"数据"和case后面的"数据"进行一一匹配,匹配成功的分支执行。按照自上而下的顺序一次匹配。匹配成功的分支执行,分支当中最后有"break;"语句的话,整switch语句终止。【注意】在这里break用来终止switch的语句执行,后面循环语句,会介绍的。
int num = 1;
switch(num){
case 1 :
System.out.println("这是星期一");
break;
case 2 :
System.out.println("这是星期二");
break;
case 3 :
System.out.println("这是星期三");
break;
default :
System.out.println("输入有误!");
}
上面代码的输出结果
这是星期一
匹配成功的分支执行,分支当中没有"break;"语句的话,直接进入下一个分支执行(不进行匹配),这种现象被称为case穿透现象。
int num = 1;
switch(num){
case 1 :
System.out.println("这是星期一");
case 2 :
System.out.println("这是星期二");
case 3 :
System.out.println("这是星期三");
default :
System.out.println("输入有误!");
}
上面代码的输出结果
这是星期一
这是星期二
这是星期三
输入有误!
所有分支都没有匹配成功,当有default的语句的话,会执行default分支当中的程序。
int num = 4;
switch(num){
case 1 :
System.out.println("这是星期一");
break;
case 2 :
System.out.println("这是星期二");
break;
case 3 :
System.out.println("这是星期三");
break;
default :
System.out.println("输入有误!");
}
上面代码的输出结果
输入有误!
* case可以合并:
int num = 0; // int num = 1;
switch(num){
case 1 : case 0 :
System.out.println("这是星期一");
break;
case 2 :
System.out.println("这是星期二");
break;
case 3 :
System.out.println("这是星期三");
break;
default :
System.out.println("输入有误!");
}
无论变量num是0 还是 1,结果都输出
这是星期一
可以使用switch语句做一个简单的计算机程序
public class SwitchTest{
public static void main(String[] args){
java.util.Scanner s = new java.util.Scanner(System.in);
System.out.println("欢迎使用简单计算器系统:");
System.out.print("输入第一个数字:");
int num1 = s.nextInt();
System.out.print("输入运算符:");
String operator = s.next();
System.out.print("输入第二个数字:");
int num2 = s.nextInt();
int result = 0;
switch(operator){
case "+" :
result = num1 + num2;
break;
case "-" :
result = num1 - num2;
break;
case "*" :
result = num1 * num2;
break;
case "/" :
result = num1 / num2;
break;
case "%" :
result = num1 % num2;
break;
default :
System.out.println("输入有误!");
break;
}
System.out.println("运算结果为:" + num1 + operator + num2 + " = " + result);
}
}
-
for 语句 --> 循环结构
- 循环结构:
在程序当中总有一些需要反复的/重复的执行的代码,假设没有循环结构,那么这段需要重复执行的代码自然是需要重复编写的。代码无法得到重复使用。所以多数编程语言都是支持循环结构的。将来把需要反复执行的代码片段放到"循环体",再联合"计数器",共同控制这段需要反复执行的代码。
//需求:输出数字1-10
System.out.println(1);
System.out.println(2);
System.out.println(3);
System.out.println(4);
System.out.println(5);
System.out.println(6);
System.out.println(7);
System.out.println(8);
System.out.println(9);
System.out.println(10);
使用循环结构来代替以上所有反复执行的代码快,可以减少代码量
//需求:输出数字1-10
for(int i = 1; i <= 10; i++){
System.out.println(i);
}
- 基本上所欲的编程语言支持的循环包括三种:
* for循环
* while循环
* do...while循环
- for循环的语法结构:
for( 初始化表达式; 布尔表达式; 更新表达式 ){
循环体 //是需要重复执行的代码片段【循环体:有java语句构成】
}
- for循环的执行原理
* 初始化表达式、布尔表达式、更新表达式都不是必须的!【但是两个分号是必须的】
//死循环
for(;;){
System.out.println("死循环");
}
* 初始化表达式最先执行,并且在整个for循环当中只执行一次。
* 布尔表达式必须是true/false,不能是其他值。
- for循环的执行过程:
先执行初始化表达式,并且该表达式只执行一次,判断布尔表达式的结果是true,还是false,如果布尔表达式true,执行循环体,执行更新表达式(重复执行判断布尔表达式的结果,执行循环体,执行更新表达式),直到布尔表达式false,循环结束。
- 以下的for循环当中"i"变量的作用域是仅限于当前for循环内部使用。
for(int i = 0; i < 10; i++){
System.out.println("i -----> " + i);
}
//这个i变量不可以在for循环外面访问
//System.out.println("i = " + i); //编译报错
- 这也是根变量 “i” 的作用域有关的
public void test(){
int i = 0; //test方法作用域当中的i变量
for(; i < 10; i++){
System.out.println("i = " + i);
}
//这里可以访问test方法作用域当中的i变量
System.out.println("i -----> " + i); //10
}
- for 循环的更新表达式 可以根据自己的需求而改变的
//输出1~10中所有的偶数
for(int i = 2; i <= 10; i += 2){
System.out.println("i ------> " + i);
}
- 也可以写成这种形式,不过一般情况下,不会这样使用的
for(int i = 0; i < 10;){
System.out.println("计数器 -----> " + i); //0 1 2 3 4 5 6 7 8 9
i++;
}
- 控制语句之间,是可以相互嵌套的
for(int i = 1; i <= 10; i++){
if(i % 2 != 0){
System.out.println("奇数 -----> " + i);
}
}
- 使用for循环嵌套,做一个九九乘法表
for(int i = 1; i <= 9; i++){ //外层循环9次
//处理当前行,将当前行中的所有的项目全部输出
for(int j = 1; j <= i; j++){
System.out.print(i + "*" + j + "=" + (i * j) + " ");
}
System.out.println(); //换行
}
foreach(也叫作 增强for循环)
- JDK5.0之后推出了一个新特性
- 语法结构:
for (元素类型 变量名 : 数组或集合) {
System.out.println(变量名);
}
可以一定程度上的简化数组和集合的输出,但是有一个缺点:没有下标
下面以数组为例,
// int类型数组
int[] arr = {432, 4, 56, 76, 45};
// 遍历数组(普通for循环)
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
for(int data : arr) {
// data就是数组中的元素(数组中的每一个元素。)
System.out.println(data);
}
-
break语句
break是java语言当中的关键字,break + ";" 可以成为一个单独的完整的java语句: break;
- break语句使用在switch语句当中,用来终止switch的语句执行。
- break语句同样可以使用在循环语句当中,用来终止循环的执行。
break;语句使用在for、while、do..while循环语句当中用来跳出循环,终止循环的执行。因为当程序循环到某个条件的时候,后续的循环没必要执行了,再执行也是耗费资源,所以可以终止循环,这样可以提高程序的执行效率。
- break终止哪个循环呢?
在默认情况下:break语句终止的是离它最近的循环语句。
for(int j = 0; j < 3; j++){
for(int i = 0; i < 10; i++){
if(i == 5){
//当前的break语句终止的是内层for循环,因为这个for离它最近,这里的break语句不会影响到外层for循环
break;
}
System.out.println("i -----> " + i);
}
System.out.println();
}
当然也可以指定终止某个循环,需要给循环起名,采用这种语法:break 循环名称;
不过这种方式,使用的很少。
//给for循环起名for1
for1:for(int j = 0; j < 3; j++){
//给for循环起名for2
for2:for(int i = 0; i < 10; i++){
if(i == 5){
break for1; //终止for1循环
}
System.out.println("i -----> " + i);
}
}
以上以for循环为例来说明break;语句。其他循环语句类似。
-
continue语句
continue也是一个continue关键字加一个分号构成一个单独的完整的java语句,主要出现循环语句当中用来控制循环的执行。
- break和continue的区别?
* break表示循环不执行了,跳出循环,终止循环。
* continue表示终止当前"本次"循环,直接进入下一次循环继续执行。
for(int i = 0; i < 10; i++){
if(i == 5){
break;
}
System.out.println("i --->" + i); //0 1 2 3 4
}
for(int i = 0; i < 10; i++){
if(i == 5){
continue; //只要这个语句执行,当前本次循环停止,直接进入下一次循环"继续"执行
}
System.out.println("i --->" + i); //0 1 2 3 4 6 7 8 9
}
continue也有这样的语法:
continue 循环名称;【作为了解】
myFor:for(int i = 0; i < 10; i++){
if(i == 5){
continue myFor;
}
System.out.println("i --->" + i); //0 1 2 3 4 6 7 8 9
}
-
while 语句 --> 循环结构
- while循环的语法结构:
while(布尔表达式){
循环体;
}
- while循环的执行原理:
先判断布尔表达式的结果,如果是true,执行循环体(重复执行,判断布尔表达式的结果,如果是true,执行循环体),直到布尔表达式的结果为false时,循环结束。
- while循环的循环次数:
0 ~ N次
【注意】:while循环的循环体可能执行次数为0次。
使用while循环的时候,一定要注意死循环,如果编译器检测到会提醒
//死循环
while(true){
System.out.println("死循环");
}
//编译器检测到该程序永远都无法被执行,所以编译报错。
System.out.println("哈哈!!!");
但是也会存在编译器检测不到是时候
int i = 10;
int j = 5;
// 死循环
while(i > j){
System.out.println("嘿嘿!!!");
}
System.out.println("哈哈!!!");
因为在编译的时候,编译器无法“得出”这两个变量究竟是那个大。
-
do...while 循环
- do..while循环的语法结构:
do{
循环体;
}while(布尔表达式);
int a = 1;
do{
System.out.println(a); //1 2 3 4 5 6 7 8 9 10
a++;
}while(a <= 10);
- do..while循环的执行原理:
先执行循环体,再判断布尔表达式的结果,如果是true(重复执行,先执行循环体,再判断布尔表达式的结果,如果是true),直到布尔表达式的结果为false时,循环结束。
- do..while循环的循环体代码片段执行次数是:1-N次【至少一次】
【注意事项】:do..while循环语句最终有一个"分号"别丢了。
与while循环语法类似,也要注意避免死循环
//死循环
int i = 1000;
do{
System.out.println(i);
}while(i > 100);
-
方法
- 方法的本质是什么?
方法就是一段代码片段,并且这段代码片段可以完成某个特定的功能,并且可以被重复使用。
通常我们使用这个方法,被称为方法的"调用/invoke"。
方法定义在类体当中,在一个类当中可以定义多个方法,方法编写的位置没有先后顺序,可以随意。【方法体当中不能再定义方法!!!!!】方法体有java语句构成,方法体当中的代码遵守自上而下的顺序一次执行。
- 定义方法的语法结构:
[修饰符列表] 返回值类型 方法名(形式参数列表){
方法体;
}
对以上的语法结构进行解释说明:
1、关于修饰符列表:
* 可选项,不是必须的
* 方法的修饰符列表当中有static关键字的话,怎么调用这个方法?
- 类名.方法名(实际参数列表);
2、返回值类型:
* 什么是返回值?
一个方法是可以完成某个特定功能的,这个功能结束之后大多数都是需要返回最终执行结果的,执行结果可能是一个具体存在的数据。而且这个具体存在的数据就是返回值。
* 返回值类型?
返回值是一个具体存在的数据数据都是有类型的,此处需要指定的是返回值的具体类型。
* 返回值类型都可以指定哪些类型呢?
java任意一种类型都可以,包括基本数据类型和所有的引用数据类型。
* 也可能这个方法执行结束之后不返回任何数据, java中规定,当一个方法执行结束之后不返回任何数据的话,返回值类型位置必须编写:void关键字。
* 返回值类型可以是:
byte,short,int,long,float,double,boolean,char,String,void....
* 返回值类型若不是void,表示这个方法执行结束之后,必须返回一个具体的数值。当方法执行结束的时候没有返回任何数据的话编译器报错。
怎么返回值呢?代码怎么写呢?
"return 值;",并且要求"值"的数据类型必须和"方法"的返回值类型一致,不然编译器报错。
* 返回值类型是void的时候,在方法体当中不能编写 "return 值;" 这样的语句。但是要注意可以编写"return;"这样的语句。
* 只要带有return关键字的语句执行,return语句所在的方法结束。【不是JVM结束,是return所在的方法结束】
3、方法名:
* 只要是合法的标识符就行
* 方法名最好见名知意
* 方法名最好的动词
* 方法名首字母要求小写,后面每个单词首字母大写
4、形式参数列表:【简称 形参】
* 形参是么局部变量:int a; double b; float c; String s;....
* 形参的个数可以是:0-N个
* 多个形参之间用"逗号"隔开
* 形参中起决定性作用的是形参的数据类型,形参的名字就是局部变量的名字。
例如:
方法定义
public static int sum(int a, int b){
}
方法调用
sum("abc", "def"); //编译器报错
sum("123", 20); //("123", 20)是实参列表
5、方法体必须由大括号括起来,方法体当中的代码有顺序,遵循自上而下的顺序依次执行。并且方法体由java语句构成,每个java语句以";"结尾。
main方法
- main方法是SUN规定的主方法,必须是固定的编写方式,也是程序的入口
//public表示公开的,class表示定义类,MethodTest是一个类名
public class MethodTest{ //表示定义一个公开的类,起名MethodTest,由于是公开的类,所以源文件名必须:MethodTest
//类体
//类体中不能直接编写java语句,除声明变量之外,方法出现在类体当中
//方法
//public表示公开的,static表示静态的,void表示方法执行结束之后不返回任何数据
//main是方法名:主方法
//(String[] args) :形式参数列表,其中String[]是一种引用数据类型,args是一个局本变量的变量名,所以以下只有args这个局部变量的变量名是随意的
//主方法就需要这样固定编写,这是程序的入口。【SUN规定的,必须这样写】
public static void main(String[] args){
//这里的程序一定会执行
//main方法是JVM负责调用的,是一个入口位置
//从这里作为起点开始执行程序
//既然是这样,我们就可以在这里编写java语句来调用其他的方法
System.out.println("main方法执行了");
}
}
- 方法的调用不一定在main方法当中,可以在其他方法当中。只要是程序可以执行到的位置,都可以去调用其他方法。
public class MethodTest01{
public static void sum(int a, int b){
System.out.println(a + " + " + b + " = " + (a + b));
MethodTest01.doSome(); //调用doSome方法
}
//主方法main
public static void main(String[] args){
MethodTest01.sum(1, 2); //调用sum方法
}
public static void doSome(){
System.out.println("Over doSome!!");
}
}
- 方法调用的时候实参和形参要求个数对应相同,数据类型对应相同。类型不同的时候要求能够进行相应的自动类型转换
public class MethodTest02{
public static void main(String[] args){
//MethodTest02.sum(); //编译报错:参数数量不同
//MethodTest02.sum(true, false); //编译错误:实参和形参的类型不对应
MethodTest02.sum(10L, 20L); //可以
MethodTest02.sum(10, 20); //存在自动类型转换:int ---> long
//MEthodTest02.sum(3.0, 20); //编译错误:参数的类型不对应
MethodTest02.sum((long)3.0, 20); //可以
}
public static void sum(long a, long b){
System.out.println(a + " + " + b + " = " + (a + b));
}
}
- 方法的调用:
1、方法的修饰符列表当中的static关键字,完整的调用方式是:类名.方法名(实参列表);
2、但是,有的时候"类名."可以省略,什么情况下可以省略呢?
m1(){
m2();
}
m1方法和m2方法在同一个类体当中的时候,"类名."可以省略不写。
3、建议在一个java源文件当中只定义一个class,比较清晰。
public class MethodTest03{
public static void main(String[] args){
MethodTest03.m(); //调用方法
m(); //对于方法的修饰符列表当中有static关键字的:"类名."可以省略不写
A.doOther(); //调用其他类【不是本类中的】中的方法
//编译报错:"类名."省略以后,默认从当前类中找"doOther"方法,在当前类中该方法不存在。
//doOther();
}
public static void m(){
System.out.println("m method execute!");
MethodTest03.m2(); //完整的方式
m2(); //省略的方式
A.m2(); //不想调用当前本类当中的m2方法,这个时候就必须添加"类名."了。
}
public static void m2(){
System.out.println("m2 execute!");
}
}
class A{
public static void doOther(){
System.out.println("A's doOther method invoke!");
}
public static void m2(){
System.out.println("A's m2 method invoke!");
}
}
【注意】:
当前行的程序在没有结束的时候,下一行代码是无法执行的。只要记住方法中的代码是自上而下的顺序依次执行即可。
public class MethodTest04{
public static void main(String[] args){
System.out.println("main begin");
m1();
System.out.println("main over");
}
public static void m1(){
System.out.println("m1 begin");
m2();
System.out.println("m1 over");
}
public static void m2(){
System.out.println("m2 begin");
m3();
System.out.println("m2 over");
}
public static void m3(){
System.out.println("m3 begin");
System.out.println("m3 over");
}
}
上面的程序结果如下:
main begin
m1 begin
m2 begin
m3 begin
m3 over
m2 over
m1 over
main over
- 方法的返回值类型不是void的时候。
* 返回值类型不是void的时候,要求方法必须保证百分百的执行"return 值;"这样的语句来完成值得返回。没有这个语句编译器会报错。
下面是return正确案例
//可以,但是具体的方法体中编写的代码无法满足当前的需求。
public static int divide(int a, int b){
return 1;
}
public static int divide(int a, int b){
int c = a / b;
return c;
}
下面是return错误案例
//编译报错:缺少返回语句
public static int divide(int a, int b){}
//编译报错:缺少返回值
public static int divide(int a, int b){
return;
}
//编译报错:方法定义的时候要求返回一个int类型,此时返回布尔类型,类型不兼容。
public static int divide(int a, int b){
return true;
}
* 一个方法有返回值的时候,当我们调用这个方法的时候,方法返回了一个值,对于调用者来说,这个返回值可以选择接收,也可以选择不接受,但是大部分情况下,我们都是选择接收到的。
public class MethodTest{
public static void main(String[] args){
//调用方法
divide(10, 3); //这里没有接收这个方法的返回数据
//这里接收一下方法执行结束之后的返回值
//采用变量接收,变量的数据类型需要和返回值的数据类型相同,或者可以自动类型转换。
//boolean i = divide(10, 3); //编译报错,类型不兼容
//赋值运算符的右边先执行,将执行结果赋值左边的变量
int i = divide(10, 3);
System.out.println(i);
long h = divide(10, 3);
System.out.println(h);
System.out.println(divide(10, 3));
}
public static int divide(int a, int b){
System.out.println("divide method!");
return a / b;
}
}
-
return关键字
- 带return关键字的java语句只要执行,所在的方法执行结束。
- 在同一个作用域当中,return语句下面不能编写任何代码,因为这些代码永远都执行不到,所以编译报错。
public static int m(){
int a = 10;
if(a > 3){
return 1;
//这里不能编写代码,编译错误,因为无法访问的语句
//System.out.println("m method!");
}
System.out.println("m method!!!!!!"); //这里的代码有可能执行到,所以编译不会报错
return 0;
//System.out.println("hello!"); //编译错误,无法访问的语句
}
- 经典return错误案例
//编译报错:缺少返回语句,以下程序编译器认为无法百分百保证"return 1;"会执行。
public static int m(){
int a = 10;
if(a > 3){
return 1;
}
}
- 上一个案例改进
//以下程序可以保证"return 1; 或 return 0;" 执行。编译通过。
public static int m(){
int a = 10;
if(a > 3){
return 1;
}
return 0;
}
- 返回值类型是void的方法当中使用"return ;"语句。
"return;"语句出现在返回值为void的方法当中,主要是为了用来结束当前方法。
public class ReturnTest{
public static void main(String[] args){
m();
for(int i = 10; i > 0; i--){
if(i == 9){
return; //结束的是main方法
}
System.out.println("i ---> " + i);
}
System.out.println("hello!!");
}
public static void m(){
for(int i = 0; i < 10; i++){
if(i == 5){
//return; //不是终止for循环,终止的是m()方法
break; // 终止的是for循环
}
System.out.println("i ---> " + i);
}
System.out.println("m method!!");
}
}
【注意】:对于结果类型为空的方法,无法返回值
public static void m(){
return;
}
-
方法内存执行分析
方法在执行过程当中,在JVM中的内存是如何分配的呢,内存是如何变化的?
1、方法只定义,不调用,是不会执行的,并且在JVM中也不会给该方法分配"运行所属"的内存空间。只有在调用这个方法的时候,才会动态的给这个方法分配所属的内存空间。
2、在JVM内存划分上有这样三块主要的内存空间(当然除了这三类之外还有其他的内存空间):
* 方法区内存
* 堆内存
* 栈内存
3、关于栈数据结构:
* 栈:stack,是一种数据结构。
* 数据结构反应的是数据的存储形态。
* 常见的数据结构:
- 数组
- 队列
- 栈
- 链表
- 二叉树
- 哈希表/散列表
....
4、方法代码片段存放在哪里?方法执行的时候执行过程的内存在哪里分配?
* 方法代码片段属于.class字节码文件的一部分,字节码文件在类加载器的时候,将其放到了方法区当中。所以JVM中的三块主要的内存空间中方法区内存最先有数据,存放了代码片段。
* 代码片段虽然在方法区内存当中只有一份,但是可以被重复调用。每一次调用这个方法的时候,需要给方法分配独立的活动场所,在栈内存中分配。【栈内存中分配方法运行的所属内存空间】
5、方法在调用的瞬间,会给该方法分配内存空间,会在栈中发生压栈动作,方法执行结束之后,给该方法分配的内存空间全部释放,此时发生弹栈动作。
* 压栈:给方法分配内存
* 弹栈:释放该方法的内存空间
6、局部变量在"方法体"中声明。局部变量运行阶段内存在栈中分配。
【重点】:
方法调用的时候,在参数传递的时候,实际上传递的是变量中保存的那个"值"传过去了。
-
方法重载
- 方法重载机制(Overload)
功能虽然不同,但是功能相似的时候,可以让出让程序员使用,这些方法的时候就像在使用同一个方法一样,这样程序员以后编写代码比较方便,也不需要记忆更多的方法名。代码也会很美观。
- 前提:
功能相似的时候,方法名可以相同。但是,功能不同的时候,尽可能让两个方法名不同。
- 什么条件满足之后构成了方法重载?
* 在同一个类当中
* 方法名相同
* 参数列表不同:
- 数量不同
- 顺序不同
- 类型不同
public class OverloadTest{
public static void main(String[] args){
m1();
m1(10);
m2(1, 2.0);
m2(2.0, 1);
m3(10);
m3(3.0);
}
//以下两个方法构成重载
public static void m1(){}
public static void m1(int a){}
//以下两个方法构成重载
public static void m2(int a, double b){}
public static void m2(double a, int b){}
//以下两个方法构成重载
public static void m3(int a){}
public static void m3(double b){}
}
- 方法重载和什么有关系,和什么没关系?
* 方法重载和方法名 + 参数列表有关系
//编译错误:以下不是方法重载,是反生了方法重复了
public static void m4(int a, int b){}
public static void m4(int b, int a){}
* 方法重载和返回值类型无关
// 以下编译报错
public static void x(){}
public static int x(){
return 1;
}
* 方法重载和修饰符无关
//编译错误:方法重载和修饰符无关
void y(){}
public static void y(){}
-
方法递归
关于方法的递归调用
1、什么是递归?
方法自身调用自身。
a(){
a();
}
2、递归是很耗费内存的,递归算法可以不用的时候尽量别用。
3、以下程序运行的时候发生了这样的一个错误【不是异常,是错误Error】
java.lang.StackOverflowError【栈内存溢出错误】
错误发生无法挽回,只有一个结果,就是JVM停止工作。
下面代码就会出现错误。
public class RecursionTest{
//主方法
public static void main(String[] args){
System.out.println("main begin");
doSome(); //调用doSome方法
System.out.println("main over");
}
//以下代码片段虽然只有一份,但是可以被重复的调用,并且只要调用doSome方法就会在栈中新分配一块所属的内存空间。
public static void doSome(){
System.out.println("doSome begin");
doSome(); //这行代码不结束,下一行程序是不能执行的
System.out.println("doSome over");
}
}
4、递归必须有结束条件,没有结束条件一定会发生栈内存溢出错误。递归即使有了结束条件,即使结束条件是正确的,也有可能发生栈内存溢出错误,因为递归的太深了。
5、递归的原理图:
上图中,红色箭头代表方法的调用,蓝色箭头代表着方法的返回。
【注意】:
递归可以不使用尽量别用。但是有些情况下该功能的实现必须依靠递归方式。
案例:使用递归,计算1~N的和【可以不用递归,尽量不用递归】
public class RecursionTest01{
public static void main(String[] args){
n = 10;
retValue = sum(n); //调用sum方法
System.out.println(retValue);
}
//单独的定义一个方法,这是一个独立的功能,可以完成1~N的求和,递归
public static int sum(int n){
if(n == 1){
return 1;
}
return n + sum(n-1);
}
}