1.Java的基本语法
1.基本格式
// 类的修饰包括:public,abstract,final 修饰符 class 类名{ 程序代码 } 例: public class Test{ public static void main(String[] args){ System.out.println("hello " + "world"); } }
- 语法说明
- 1.
Java
程序代码都必须放在类中,类需要用class
关键字定义,class
前面可以有修饰符(public
,abstract
,final
)- 2.
main
函数:主函数是程序的入口,想要执行的代码必须写入主函数中,因为JVM
执行时首先执行main
方法,且写法是固定的(一个类中只能有一个主函数)- 3.
String[] args
:main
函数的参数,可使用数组形式也可使用可变参数形式,可利用该参数在启动时给程序传递指定的参数,多个参数用空格
分隔
- 4.中括号
{}
:划分代码块的边界- 5.
Java
源文件后缀名为.java
- 6.当类不是定义在
java.lang
等基本包中时,要使用import
将相应的包加载到当前类中- 7.
Java
程序中一句连续的字符串不能分开在两行中书写,可以使用加号+
将连续的字符串分成两个字符串在不同行书写
2.基本规范
- 1.
Java
中类名,接口名采用大驼峰命名法:所有单词的首字母大写- 2.变量名,函数名采用小驼峰命名法:从第二个单词开始首字母大写
- 3.常量名所有字母都大写,多个单词之间用下划线
_
隔开- 4.包名全小写,且只能放在当前文件有效代码的第一行
- 5.望名知意
2.Java中的输入输出
1.输出
- 1.打印输出到
标准输出流
(控制台窗口)/** 通过调用打印流printStream中的方法进行输出 */ //输出内容单独一行,输出内容可以为空 System.out.println(); //输出内容不换行,输出内容不能为空 System.out.print();
2.输入
- 1.
JDK5.0
之后可通过控制台读取输入public static void main(String[] args) { // 利用Scanner类,其定义在java.util包中 // 首先创建一个Scanner对象并传入标准输入流System.in Scanner in = new Scanner(System.in); String next = in.next(); System.out.println(next); }
3.Java中的注释
- 1.
Java
中的注释有3
种类型
1.单行注释
//单行注释
2.多行注释
/* 多行注释 */
3.文档注释
/** * 文档注释 */ 例: /** * 这是类外部 */ package net.csdn.editor; public class Test{ /** * 这是方法外部,类内部 */ public static void main(String[] args){ /** * 这是方法内部 */ System.out.println("test"); } }
//带包编译 javac -d . Test.java //解释执行 java net.csdn.editor.Test; //生成说明文档 javadoc -d 文件名 源文件名.java //文件名是文档生成的目标路径,设为.表示在当前路径下生成 javadoc -d . Test.java
- 语法说明
- 1.
-d
表示带包,.
表示在当前文件中生成,可使用其他文件名,如果文件不存在会自动生成- 2.注释是对程序的功能或代码的解释说明
- 3.注释只在
Java
源文件中有效,不参与代码的编译和运行(编译程序时,编译器会忽略这些注释信息,不会将其编译到class
字节码文件中去)- 4.文档注释用来对某一个函数或者某个类添加说明
- 5.文档注释可以使用
javadoc
命令将文档注释提取出来生成帮助文档- 6.只有写在类外部和方法外部,类内部的文档注释才有作用
4.Java中的标识符
- 1.
Java
中需要定义符号用来标记名称,这些符号被称为标识符- 2.标识符可以由字母,数字,下划线(_)和美元符号($)或人民币符号(¥)组成,但不能以数字开头,也不能是
Java
中的关键字或者保留字
5.Java中修饰符
- 1.访问控制修饰符
- 1.
dufault
- 2.
public
- 3.
protected
- 4.
private
- 2.非访问控制修饰符
- 1.
final
- 2.
abstract
- 3.
static
- 4.
synchronized
6.Java中的关键字
- 1.关键字是编程语言里事先定义好并赋予了特殊含义的单词
abstract assert boolean break byte case catch char class const continue default do double else enum extends final finally float for goto if implements import import instanceof int interface long new package private protected public return short static stricttfp super switch synchronized this throw throws transient try void volatile while - 语法说明
- 1.所有的关键字都是小写
- 2.程序中的标识符不能以关键字命名
- 3.
const
和goto
是保留关键字,虽然在Java
中还没有任何意义,但是在程序中不能作为自定义的标识符- 4.
true
,false
,null
不属于关键字,它们是一个单独标识类型,不能直接使用
7.Java中的常量
- 1.常量是程序中固定不变,不能改变的数据,
Java
中常量用final
修饰- 2.常量一般采用大写字母,不同单词间用
_
隔开- 3.一般推荐使用
static final
修饰常量,如果不加则每次new
一个对象都会为该常量重新分配一次内存(创建类的多个对象时,用static
修饰的常量在内存中只有一份拷贝,不用static
修饰则可能有多份拷贝)
- 1.不管静态常量还是普通常量只要是基本类型就不能在初始化后重新修改其值
- 2.对象类型普通常量、静态常量可以在初始化后修改其对象状态,但不可以改变其引用
1.常量类型
- 1.整型常量:整数类型的数据(包括二进制,八进制,十进制,十六进制
4
种表现形式)
- 二进制: 由
0b
或0B
开头,并以数字0
和1
组成的数字序列- 八进制: 由
0
开头,并以数字0 ~ 7
组成的数字序列- 十进制: 以数字
0 ~ 9
组成的数字序列- 十六进制: 由
0x
或0X
开头,并以0 ~ 9,A ~ F
(包括0
和9
,A
和F
,字母不区分大小写)组成的数字序列- 2.浮点数常量
- 1.浮点数常量分单精度浮点数和双精度浮点数两种类型
- 2.单精度浮点数必须以
F
或者f
结尾- 3.双精度浮点数可以以
D
或d
结尾- 4.使用浮点数时不加任何后缀,虚拟机会默认为
double
双精度浮点数- 3.字符常量
- 1.字符常量表示一个字符,一个字符常量要用一对英文单引号
''
引起来,可以是英文字母,数字,标点符号以及由转义字符来表示的特殊字符- 2.
Java
采用的是Unicode
字符集,Unicode
字符以\u
开头,空白字符在Unicode
码表中对应的值为\u0000
- 4.字符串常量
- 1.字符串常量表示一串连续的字符,一个字符串常量要用一对英文双引号
""
引起来- 2.一个字符串可以包含一个字符或多个字符,也可以不包含任何字符
- 5.布尔常量
- 1.布尔常量的两个值
true
和false
,用于区分一个事物的真与假- 6.
null
常量
- 1.
null
常量只有一个值null
,表示对象的引用为空
2.字符集
- 1.字符(
Character
):各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等- 2.字符集(
Character set
):多个字符的集合,字符集是一张码表,它规定了文字与数字的一一对应关系,与计算的内部表示没有必然的联系,字符集种类较多,每个字符集包含的字符个数不同- 3.常见字符集名称
- 1.
ASCII
字符集- 2.
GB2312
字符集- 3.
GB18030
字符集- 4.
Unicode
字符集等
3.进制转换
- 1.十进制和二进制之间的转换
- 十进制转换成二进制
- 将要转换的数除以
2
,得到商和余数,将商继续除以2
,直到商为0
,最后将所有余数倒序排列,得到的数就是转换结果- 二进制转换成十进制
- 从右到左用二进制位上的每个数去乘以
2
的相应次方(次方从0
开始),然后把所有相乘后的结果相加,得到的数就是转换结果- 2.二进制和八进制的转换
- 八进制转换为二进制
- 将要转换的数除以
2
,得到商和余数,将商继续除以2
,直到商为0
,最后将所有余数倒序排列,得到的数就是转换结果- 二进制转换为八进制
- 从右到左每三位为一组,如果最左边不够三位用
0
补齐,然后将每一组从左到右乘以2
的相应次方(次方从0
到2
),最后把所有相乘后的结果相加,得到的数就是转换结果- 3.二进制和十六进制的转换
- 十六进制转换为二进制
- 将要转换的数除以
2
,得到商和余数,将商继续除以2
,直到商为0,最后将所有余数倒序排列,得到的数就是转换结果- 二进制转换为十六进制
- 从右到左每四位为一组,如果最左边不够四位用
0
补齐,然后将每一组从左到右乘以2
的相应次方(次方从0
到2
),最后把所有相乘后的结果相加,得到的数就是转换结果
8.Java中的变量
- 1.变量:是计算机内存中的一块存储空间,是存储数据的基本单元
- 2.变量名:是标识变量(内存单元)的标识符
- 3.变量值:是变量(内存单元)中存储的数据
1.变量的创建方式(4种)
- 1.先声明再赋值
数据类型 变量名; 变量名 = 值; 例:int x; x=5;
- 2.声明的同时直接赋值
数据类型 变量名 = 值; 例:int x = 5;
- 3.同时声明多个变量,之后再一一赋值
数据类型 变量名1,变量名2,...; 变量名1 = 值1; 变量名2 = 值2; 。。。 例:int x,y,z; x = 5;y = 6;z = 7;
- 4.同时声明多个变量并赋值
数据类型 变量名1 = 值1,变量名2 = 值2 ... ; 例:int x = 1,y = 2, z = 3;
2.变量的数据类型
- 1.
Java
是一门强类型的编程语言,定义变量时必须声明变量的数据类型- 2.
Java
中变量的数据类型分为两种:基本数据类型(八种) 和 引用数据类型
- 3.基本数据类型的十进制数值范围保存在其对应包装类中
3.变量类型的范围
1.整数类型变量
类型名 占用空间 取值范围 十进制取值范围 默认值 byte(字节型) 1B/8位 -27 ~ 27-1 -128 ~ 127 0 short(短整型) 2B/16位 -215 ~ 215-1 -32768 ~ 32767 0 int(整型) 4B/32位 -231 ~ 231-1 -2147483648 ~ 2147483647 0 long(长整型) 8B/64位 -263 ~ 263-1 -9223372036854775808 ~ 9223372036854775807 0L 2.浮点数类型变量
类型名 占用空间 取值范围 默认值 float(单精度浮点数) 4B/32位 1.4E-45 ~ 3.4E+38 , -3.4E+38 ~ -1.4E-45 0.0f double(双精度浮点数) 8B/64位 4.9E-324 ~ 1.7E+308 , -1.7E+308 ~ 4.9E-324 0.0d 3.字符类型变量
类型名 占用空间 取值范围 默认值 char 2B/16位 0~65535(十六位二进制的Unicode字符) u0000 语法说明:
- 1.字符类型变量的创建方式
//1.直接赋字符 char 变量名 = '值'; //2.赋0~65535的整数值 char 变量名 = 整数值; //3.利用16进制Unicode编码赋值 char 变量名 = 'Unicode编码值';
- 2.第一种赋值方式,单引号内部只能放一个字符
- 3.第二种赋值方式,整数范围不能超过取值范围
- 4.Java中每个char类型的字符变量都会占用2个字节
- 5.char类型的变量赋值时,需要用英文单引号
''
把字符括起来,也可以将char类型的变量赋值为0~65535范围内的整数,计算机会自动将这些整数转化为所对应的字符4.布尔类型变量
类型名 占用空间 取值范围 boolean 如果boolean是单独使用:boolean占4个字节;如果boolean是以“boolean数组”的形式使用:boolean占1个字节 true,false 语法说明:
- 1.boolean(布尔)类型有两个值:false和true,用来判定逻辑条件;这两个值不能与整数进行相互转换
4.变量的作用域
- 1.Java中的变量可以分为类变量,全局变量(成员变量)和局部变量
1.静态变量
- 1.静态变量是使用
static
修饰的成员变量,也叫类变量,其具有唯一性和共享性,一般用于存储整个程序都需要使用的数据- 2.静态变量指在类中定义的一个变量,其属于类而不是实例,即无论创建多少个类实例,静态变量在内存中只有一份拷贝,所有该类的实例共享同一个类变量的值
- 3.类变量在类加载时被初始化,且只初始化一次,并且其初始化顺序与定义顺序有关(即静态变量定义在使用后也行,但如果一个静态变量依赖于另一个静态变量,那么其必须在后面定义)
- 4.静态变量的生命周期与程序的生命周期一样长,即在类加载时被创建并在整个程序运行期间都存在直到程序结束才会被销毁;因此静态变量可用来存储整个程序都需要使用的数据(配置信息、全局变量等)
- 5.常量与静态变量的区别
- 1.常量是用
final
关键字修饰的变量,一旦被赋值就不能再修改,但是多个对象可能会存在多个拷贝;静态变量是用static
关键字修饰的变量,静态变量只会在类加载时初始化一次,所有对象共享一份内存数据- 2.常量在编译时就已确定了值,而静态变量的值可在运行时改变
- 3.常量通常用于存储一些固定的值(数学常数、配置信息等),而静态变量通常用于存储可变的数据(计数器、全局状态等)
- 6.静态变量的线程安全性
- 1.
Java
中的静态变量是属于类而不是对象的实例;因此当多个线程同时访问一个包含静态变量的类时需要考虑其线程安全性- 2.静态变量在内存中只有一份拷贝,被所有实例共享;因此如果一个线程修改了静态变量的值,那么其他线程在访问该静态变量时也会看到修改后的值;这可能会导致并发访问的问题,因为多个线程可能同时修改静态变量,导致不确定的结果或数据一致性问题
- 3.为了确保静态变量的线程安全性需要采取适当的同步措施(同步机制、原子类或
volatile
关键字),以便在多线程环境中正确地读取和修改静态变量的值
2.成员变量
- 1.直接在类中声明的变量叫成员变量(又称全局变量)
- 2.如果未对成员变量设置初始值,则系统会根据成员变量的类型自动分配初始值
- 3.成员变量定义后,其作用域是其所在的整个类
- 4.成员变量的定义没有先后顺序(定义在使用后也行,但如果一个成员变量依赖于另一个成员变量,那么其必须在后面定义),但最好将成员变量的定义集中在类的顶部
class Test1{ private static byte a1; private static short a2; private static int a3; private static long a4; private static float a5; private static double a6; private static char a7; private static boolean a8; private static String a9; public static void main(String[]args){ System.out.println(a1); System.out.println(a2); System.out.println(a3); System.out.println(a4); System.out.println(a5); System.out.println(a6); System.out.println(a7); System.out.println(a8); System.out.println(a9); } }
3.局部变量
- 1.局部变量包括形参,方法中定义的变量,代码块中定义的变量
- 2.局部变量没有默认值,使用前必须显示初始化或赋值
* 3.局部变量只能用final修饰符修饰
* 4.局部变量的作用域范围从定义的位置开始到其所在语句块结束
* 5.如果局部变量的变量名和全局变量的变量名相同,则在局部变量的作用范围内全局变量暂时失效,如果在局部变量的作用范围内想访问该成员变量,则必须使用关键字this
类引用成员变量
- 注意:基本数据类型的变量传递的是值,而引用数据类型的变量传递的是引用所指的地址(对值的修改不会影响本身的数据,而对堆地址中的数据进行修改,指向该地址的引用的数据值都会改变)
public class Test01 { public static void main(String[] args) { //基本数据类型的变量 int a = 10; //引用数据类型的变量 MyClass b = new MyClass(); Test01 t = new Test01(); //输出原本的值 System.out.println(a); System.out.println(b.value); //调用指定方法 t.test(a,b); //输出调用方法后的值,其中基本数据类型的变量值没有发生变化,引用数据类型的变量值发生了变化 //基本数据类型变量依旧没有变化 System.out.println(a); //引用数据类型的变量值发生了变量 System.out.println(b.value); } //进入方法,首先给形参赋值:int a = 10;然后给MyClass b赋予上面引用指向的地址 public void test(int a,MyClass b) { //局部变量a的值发生变量,但是没有影响到原本变量 a = 20; //引用类型变量指向的对地址的数据值发生变量,会影响到指向该对地址的引用的数据值 b.value = 100; } } class MyClass{ int value = 50; }
5.变量的线程安全
静态变量:线程非安全。
加static关键字的变量,只能放在类里,不能放到方法里。
静态变量有默认初始化值。
静态变量表示所有实例共享的一个属性,位于方法区,共享一份内存,而成员变量是对象的特殊描述,不同对象的实例变量被分配在不同的内存空间,一旦静态变量被修改,其他对象均对修改可见,故线程非安全。
实例变量:单例模式(只有一个对象实例存在)线程非安全,非单例线程安全。
成员变量(实例变量):
1、成员变量定义在类中,即类中的普通变量,在整个类中都可以被类中方法所访问(如过和局部变量重名,需用this关键字)。
2、成员变量随着对象的建立而建立,随着对象的消失而消失,存在于对象所在的堆内存中
成员变量有默认初始化值
实例变量为对象实例私有,在虚拟机的堆中分配,若在系统中只存在一个此对象的实例,在多线程环境下,被某个线程修改后,其他线程对修改均可见,故线程非安全;如果每个线程执行都是在不同的对象中,那对象与对象之间的实例变 量的修改将互不影响,故线程安全。
成员变量和类变量的区别:
1、两个变量的生命周期不同
成员变量随着对象的创建而存在,随着对象的回收而释放。
静态变量随着类的加载而存在,随着类的消失而消失,且优先于对象存在。
2、调用方式不同
成员变量只能被对象调用。
静态变量可以被对象调用,还可以被类名调用。
3、数据存储位置不同
成员变量存储在堆内存的对象中,是对象的特有数据。
静态变量数据存储在方法区(共享数据区)的静态区
static还可以修饰方法,静态方法只能访问静态变量,不可以访问成员变量,因为静态方法加载时,优先于对象存在,所以没有办法访问对象中的成员。同时静态方法中不能使用this,super关键字,因为this代表当前对象,而静态方法在时,有可能没有对象,所以this无法使用。
由于每个线程执行时将会把局部变量放在各自栈帧的工作内存中,线程间不共享,故不存在线程安全问题。
1.静态变量
- 1.只要有修改变量值的操作,无论是在单例或非单例都是线程不安全的
- 2.如果线程只是读取变量的值,而不会改变变量的值,这种情况下则是线程是安全的
- 3.产生线程安全问题的原因:
- 1.静态变量只在类加载时初始化一次,且其位于方法区被所有对象共享
- 2.由于共享一份内存,一旦静态变量被修改,其他对象均对修改可见,故线程非安全
- 4.如图所示执行流程:
- 1.当线程
1
执行了number = 1; number = 2;
后,线程2
获得执行权执行number = 1;
- 2.然后当线程
1
获得执行权执行打印第二次获取number
时; 输出结果为获取第二次number = 1
- 5.解决方式
- 1.使用同步方法:将
increment()
方法声明为synchronized
,这样一次只有一个线程能够执行该方法,从而保证线程安全性
- 2.使用同步代码块:使用
synchronized
关键字创建同步代码块,只有一个线程能够进入代码块执行
- 3.使用原子类:
Java
提供了java.util.concurrent.atomic.AtomicInteger
类,可以保证对整数类型的原子操作
- 4.使用
volatile
关键字:修饰静态成员变量,从而保证可见性,volatile
关键字保证了每次读取该变量的值时都是最新的,而不是使用缓存的值
2.成员变量
- 1.如果线程只读取变量的值而不改变,则无论是单例还是非单例都是线程安全的
- 2.如果有修改变量值的操作,则单例模式因为只有一个对象实例
singleton
存在,多线程同时操作时是不安全的,而非单例模式下多线程操作是安全的- 3.实例变量为对象实例私有,分配在虚拟机的堆(
heap
)中- 4.若在系统中只存在一个对象的实例,则多线程环境下类似静态变量在被某个线程修改后,其他线程对修改均可见,故线程非安全(如
springmvc controller
是单例非线程安全的)- 5.如果每个线程执行都是在不同的对象中,那对象与对象之间的实例变量的修改将互不影响,故线程安全(如
struts2 action
默认是非单例的,每次请求在heap
中new
新的action
实例,故struts2 action
可用成员变量)
3.局部变量
- 1.局部变量的作用域在方法内部,其存放在虚拟机栈中
- 2.如果一个变量需要跨越方法的边界就必须创建在堆中
- 3.每个线程都有自己独立的调用栈,因此不同的线程可同时用不同的参数调用相同的方法
- 4.
Java
方法内部的局部变量不存在并发问题,因为每个线程都有自己的调用栈,局部变量保存在线程各自的调用栈里,不会共享自然也不存在并发问题- 5.线程封闭:方法中的局部变量因为不会和其他线程共享所以不会存在并发问题,这种解决问题的技术也叫做线程封闭(官方解释:仅在单线程内访问数据,由于不存在共享,所以即使不设置同步也不会出现并发问题)
9.Java类型转换
- 1.自动类型转换(隐式类型转换)
- 1.指两种数据类型在转换的过程中不需要显式地进行声明
- 2.自动类型转换必须同时满足两个条件:
- 1.两种数据类型彼此兼容
- 2.目标类型的取值范围大于源类型的取值范围
- 3.数据类型大小排序:byte < char < short < int < long < float < double
- 1.char类型默认转换为int类型的数据,如果将char转换为byte或short需要进行强转
- 2.float类型转换为long类型需要强转,而long类型转换为float则不需要
- 2.强制类型转换(显示类型转换)
- 1.指两种数据类型之间的转换需要进行显式的声明
- 2.当两种类型彼此不兼容,或者目标类型取值范围小于源类型时,自动类型转换无法进行,此时需要强制类型转换
目标类型 变量 = (目标类型)值
- 3.强制类型转换可能会发生数据精度缺失
class Test1{ public static void main(String[]args){ byte a; int b = 299; a = (byte)b; System.out.println(b); System.out.println(a); } } /* 原理: int类型4个字节,32位 299转换为2进制为00000000 00000000 00000001 00101011 byte类型占1个字节,8位 将int类型的数据强转为byte类型 会丢失前面多余24位,变为00101011即为43 */
- 4.Java中把浮点数类型强转为整数类型,会直接舍弃小数位,只保留整数位,且不会四舍五入
- 3.自动类型提升
- 1.只发生在数学运算期间
- 2.当运算中有高精度类型的数值参与,则结果会自动提升为高精度类型
- 3.
Java
中如果比int
类型小的类型做运算,Java
在编译时就会将它们统一强转成int
类型;当是比int
类型大的类型做运算,会自动转换成它们中的最大类型
class Test1{ public static void main(String[]args){ double a = 15.0; float b = 14.0f; float y = a+b+12+23; System.out.println(y); } }
- 注意:
- 1.任意数据类型加上双引号或者拼接双引号都会变成String字符串类型
- 2.程序从上到下,从左到右运行
class Test1{ public static void main(String[]args){ byte a = 13; byte b = 14; int x = 5; int y = 6; char m = 'a'; char n = 'A'; System.out.println(m+n+"A"); System.out.println("A"+m+n); System.out.println(a+b+"A"); System.out.println("A"+x+y); } }
10.Java中的运算符
1.算术运算符:
运算符 运算 范例 结果 + 正号 +3 3 - 负号 -4 -4 + 加 5+5 10 - 减 10-5 5 * 乘 3*4 12 / 除 5/5 1 % 取模(求余) 7%5 2 ++ 自增(前) a=2;b=++a; a=3,b=3 ++ 自增(后) a=2;b=a++ a=3,b=2 - - 自减(前) a=2;b=–a; a=1,b=1 - - 自减(后) a=2;b=a–; a=1,b=2
- 语法说明:
- 1.自增自减运算时,如果运算符++或- -放在操作数的前面则先进行自增或自减运行,再进行其他运算;如果运算符放在操作数的后面则是先进行其他运算再进行自增或自减运算
- 2.除法运算时,当除数和被除数都为整数时,结果也是一个整数;如果除法运算有小数参与,得到的结果会是一个小数(相当于自动类型提升)
- 3.取模运算时,运算结果取决于被模数(%左边的数)的符号,与模数(%右边的符号无关)
2.赋值运算符:
运算符 运算 范例 结果 = 赋值 a=3;b=2; a=3;b=2; += 加等于 a=3;b=2;a+=b; a=5;b=2; -= 减等于 a=3;b=2;a-=b; a=1;b=2; *= 乘等于 a=3;b=2;a*=b; a=6;b=2; /= 除等于 a=3;b=2;a/=b; a=1;b=2; %= 模等于 a=3;b=2;a%=b; a=1;b=2;
- 语法说明:
- 1.变量赋值时,如果两种类型彼此不兼容,或者目标类型取值范围小于源类型时,需要进行强制类型转换
- 2.但是如果使用
+=,-=,*=,/=,%=
运算符进行赋值时,强制类型转换会自动完成,程序不需要做任何显式地声明(虽然b+=a的结果为int类型,但是Java虚拟机会自动完成强制类型转换)
3.比较运算符
运算符 运算 范例 结果 == 相等于 4 == 3 false != 不等于 4 != 3 true < 小于 4<3 false > 大于 4>3 true <= 小于等于 4<=3 false >= 大于等于 4>=3 true
- 语法说明:
- 1.基本数据类型的变量存放在虚拟机栈的局部变量表中的是值,
==
在比较基本数据类型时,比较的是变量值是否相同- 2.引用数据类型的变量存放在虚拟机栈的局部变量表中的是对象引用/堆地址,比较的是堆地址是否相同
4.逻辑运算符
运算符 运算 范例 结果 & 与 true&false false | 或 true false ^ 异或 true ^ true,true ^ false false,true ! 非 !true false && 短路与 true&&true true || 短路或 true||false true
- 语法说明:
- 1.使用
&
和|
进行运算时,不论左边为true或者为false,右边的表达式都会进行运算- 2.如果使用
&&
进行运算,当左边为false时,右边的表达式则不会运算;如果使用||
进行运算,当左边为true时,右边的表达式不会运算package test.com.wd.test; public class Test { public static void main(String[] args) { // TODO Auto-generated method stub int x=0; int y=0; int z=0; boolean a,b; a = x>0 & y++>1; System.out.println(a); System.out.println("y="+y); System.out.println("----------------------------"); a = x>0 && y++>1; System.out.println(a); System.out.println("y="+y); System.out.println("----------------------------"); b = x==0 | z++>1; System.out.println(b); System.out.println("z="+z); System.out.println("----------------------------"); b = x==0 || z++>1; System.out.println(b); System.out.println("z="+z); } }
5.三元运算符:
运算符 运算 范例 结果 判断条件?表达式1:表达式2; 类似if-else 3>5?“对”:“错” 错 package test.com.wd.test; public class Test{ public static void main(String[] args) { String str = 3>6?"对":"错"; System.out.println(str); } }
6.位逻辑运算符:
运算符 运算 范例 结果 & 按位与运算 4&5 4 | 按位或运算 4 5 ^ 按位异或运算 4^5 1 ~ 按位反运算 ~4 -5 >> 右移运算符 8>>2 2 >>> 无符号右移运算符 -8>>>2 1073741822 << 左移运算符 8<<2 32
- 1.位与运算符: 将数字转换为二进制,低位对齐,高位不足的补
0
;如果对应的二进制位同时为1
,则结果为1
,否则为0
,最后将二进制转换为十进制- 2.位或运算符: 将数字转换为二进制,低位对齐,高位不足的补
0
;如果对应的二进制位有一个为1
,则结果为1
,如果对应的二进制都为0
,结果才为0
,最后将二进制转换为十进制- 3.位异或运算符: 将数字转换为二进制,低位对齐,高位不足的补
0
;如果对应的二进制位不同,则结果为1
,如果对应的二进制相同,结果才为0
,最后将二进制转换为十进制- 4.位取反运算符: 将数字转换为二进制,将二进制中的
1
改为0
,将0
改为1
,最后将二进制转换为十进制- 5.右移运算符: 将数字转换为二进制,将其二进制数组向右移动,即等价于十进制的除
2
的n次方,向右移动一位则除以2
的1
次方,正数左边第一位补0
,负数补1
- 6.无符号右移运算符: 正数的运算同右移运算法相同,无符号右移运算符和右移运算符的主要区别在于负数的计算,因为无符号右移是高位补
0
,移多少位补多少个0
;负数无符号右移,负数的二进制是正数的二进制取反再加1,例:-6
的二进制是6
的二进制取反再加1
,6
的二进制是0000 0000 0000 0000 0000 0000 0000 0110
,取反后加1
为1111 1111 1111 1111 1111 1111 1111 1010
,右移三位0001 1111 1111 1111 1111 1111 1111 1111
- 7.左移运算符: 将数字转换为二进制,将其二进制数组向左移动,即等价于十进制的乘
2
的n次方,向左移动一位则乘以2
的1
次方,正数左边第一位补0
,负数补1
扩展:原码,反码,补码
- 原码: 一个整数按照绝对值大小转换成的二进制数,称为原码
- 反码: 将二进制数按位取反,所得的新二进制数称为原二进制数的反码
- 补码: 反码加
1
称为补码- 计算机中负数以原码的补码形式表达
12.java中的选择结构语句
1.if语句
- 基本语法:
if(判断语句){ //代码块 }
2.if-else语句
- 基本语法:
if(判断语句){ //代码块 }else{ //代码块 }
3.多重if-else语句
- 基本语法:
if(判断语句1){ //执行语句1 }else if(判断语句2){ //执行语句2 } ... else if(判断语句n){ //执行语句n }else{ //执行语句n+1; }
- 语法说明:
- 1.多重if-else语句是一个整体,只能执行其中一个分支语句,一旦进入一个,其他的分支则没用,比较顺序从上到下
- 2.注意判断语句的使用方式,同样的数值,不同的使用方式,结果不同
package test.com.wd.test; public class Test{ public static void main(String[] args) { int key = 75; if(key>100) { System.out.println("A"); }else if(key>90) { System.out.println("B"); }else if(key>80) { System.out.println("C"); }else if(key>60) { System.out.println("D"); }else { System.out.println("E"); } System.out.println("------------"); if(key<100) { System.out.println("A"); }else if(key<90) { System.out.println("B"); }else if(key<80) { System.out.println("C"); }else if(key<60) { System.out.println("D"); }else { System.out.println("E"); } } }
4.switch条件语句
- 基本语法:
switch(表达式){ case 目标值1: 执行语句1; break; case 目标值2: 执行语句2; break; ...... case 目标值n: 执行语句1; break; default: 执行语句n+1; break; }
package com.entity; import java.util.Random; public enum Color { RED("red color",0), GREEN("green color",1), BLUE("blue color",2), YELLOW("yellow color",3), BLACK("BLACK color",4); Color(String name,int id){ name = name; id = id; } private String colorName; private int id; public String getColorName(){ return colorName; } public int getId(){ return id; } public static Color getColor(int max){ Random random = new Random(System.currentTimeMillis()); int num = random.nextInt(max); switch(num){ case 0: return Color.RED; case 1: return Color.GREEN; case 2: return Color.BLUE; case 3: return Color.YELLOW; default: return Color.BLACK; } } }
package com.wd.test; import com.entity.Color; public class Test02 { public static void main(String[] args) { int length = Color.values().length; Color color = Color.getColor(length); switch (color){ case RED: System.out.println("select " + "RED"); break; case GREEN: System.out.println("select " + "GREEN"); break; case BLUE: System.out.println("select " + "BLUE"); break; case YELLOW: System.out.println("select " + "YELLOW"); break; default: System.out.println("select " + "BLACK!!!"); break; } } }
- 语法说明:
- 1.switch语句中,表达式和目标值都不支持null且不能不写,否则会出现编译错误;表达式和目标值的类型需要兼容,否则也会出现编译错误;目标值只能声明为常量表达式,不能声明为范围,比较结果,变量
- 2.case语句的目标值不能重复,对于字符串类型也一样;注意:字符串可以包含Unicode转义字符,而case重复值的检查是在Java编译器对Java源代码进行相关的词法转换之后进行的;即:有些目标值在源代码中不同,但是经过词法转换后是一样,就会造成编译错误(例:“男"和”\u7537"会发生编译错误)
- 3.JDK1.5之前,switch语句只支持byte,short,char,int四种数据类型
- 4.JDK1.5及之后,switch语句支持了枚举类与byte,short,char,int的包装类(注意:这种说法不完全正确,之所以switch能够支持相应的包装类,是因为JDK1.5开始支持自动拆/装箱,编译器会自动调用相应的xxxValue()方法将其转换为int类型;之所以支持枚举类,是因为枚举类会调用ordinal()方法,该方法返回的是int类型)
- 5.Java1.7及之后,switch语句支持使用String类型,实际底层调用的是String类型的hashCode()方法和String类型重写的equals方法(注意:该特性是在编译器层次上实现,在Java虚拟机和字节码层次上还是只支持在swicth语句中使用与整数类型兼容的类型,且在case子句对应的语句块中仍需要使用String的equals方法进行字符串比较,因为哈希函数在映射的时候可能存在冲突,结合equals方法可以解决冲突)
- 6.switch中可以使用枚举替代String类型,可以提高可读性和可维护性
- 7.case之后最好带上break(因为case比较的结果为布尔类型,如果有一个case结果为true,其余case不会再进行布尔判断),直接会从进入点开始一直往下执行,直到进入default后退出switch语句,无法起到分支的作用;且default类似于if-else分支中的else,如果上述case分支都没有结果则执行default中的内容,default也可以省略,建议加上,default后的break可加可不加,没有影响(此情况适用于default在switch最后,如果default位置在switch语句的其他位置,则最好加上break,否则也会从default往下一直执行到程序结束)
- 8.如果switch-case结构中的多个case的执行语句相同,则可以考虑进行合并,提高效率
@Test public void test02(){ int score = 78; switch(score/10){ case 1: case 2: case 3: case 4: case 5: System.out.println("不及格!"); case 6: case 7: case 8: case 9: case 10: System.out.println("及格!"); } //优化 switch (score/60){ case 0: System.out.println("不及格"); case 1: System.out.println("及格"); } }
- 9.凡是可以使用switch-case的结构,都可以转换为if-else,反之不成立,因为switch对表达式和目标值有限制要求
5.三元运算符,if-else,switch-case的区别
- 1.凡是可以使用三元运算符(三元运算符内部只能是表达式,所以不能代替if else,而if else能取代任何有三元的地方)和switch-case结构的都可以转换为if-else
- 2.但是反之不一定成立,如果可以使用三元运算符或者switch-case则使用他两,因为效率更高
13 java中的循环语句
1.定义:
- 在某些条件满足的情况下,反复执行特定代码的功能
2.循环语句分类
- while循环语句
- do…while循环语句
- for循环语句
3.循环语句的四个组成部分
- 初始化部分
- 循环条件部分,必须是boolean类型
- 循环体部分
- 迭代部分
1.while循环语句
- 基本语法:
while(循环条件){ 执行语句; ... }
- 语法说明:
- 1.while语句会反复地进行条件判断,只要条件成立,{}内的执行语句就会执行,直到条件不成立,while循环结束
- 2.{}中执行语句被称作循环体,循环体是否执行取决于循环条件,当循环条件为true,循环体就会执行,循环体执行完毕时会继续判断循环条件,如条件仍为true则会继续执行,直到循环条件为false,整个循环过程才会结束
- 3.while循环适用于循环次数不确定的情况下
2.do…while()循环语句
- 基本语法:
do{ 执行语句; ... }while(循环条件);
- 语法说明:
- 1.关键字do后面{}中的执行语句是循环体
- 2.do…while循环语句将循环条件放在了循环体的后面。即:循环体会无条件执行一次,然后在根据循环条件来决定是否继续执行
- 3.while和do…while的区别:如果循环条件在循环语句开始时就不成立,那么while循环的循环体一次都不会执行,而do…while循环的循环体还是会执行一次
3.for循环语句
- 基本语法:
for(初始化部分;循环条件部分;迭代部分){ 循环体部分 }
- 语法说明:
- 1.for循环的执行顺序:初始化部分–>循环条件部分(满足条件)–>循环体部分–>迭代部分–>循环条件部分(满足条件)–>…–>(循环条件部分不满足)–>结束循环
- 2.for循环的循环条件部分必须为boolean类型,所以只能有一个整体结果为boolean类型的循环条件,也可以没有,如果没有循环条件部分,即成为死循环
- 3.for循环的其他部分都可以存在多个语句,对循环本身没有影响,多个语句之间用逗号分隔;而不同部分用分号分隔
- 4.for循环
()
中的三部分内容都可以省略,但是必须通过;
进行语法占位,此时为无限死循环
- 5.for循环语句一般用在循环次数已知的情况下
4.嵌套循环
- 基本语法:
for(初始化部分;循环条件部分;迭代部分){ 循环体部分; ... for(初始化部分;循环条件部分;迭代部分){ 循环体部分; ... } ... }
/** * @Author 依山观澜 * @Date 2021/9/12 8:43 * @Description 质数(素数):只能被1和它本身整除的自然数,1不是质数;即从2开始,到这个数-1结束为止,都不能被这个数整除 * @Since version-1.0 */ public class Test01 { public static void main(String[] args){ //标识i是否被j除尽,一旦除尽,即不是质数,修改其值 boolean isFlag = true; //计数器:用于记录质数的个数 int sum = 0; //获取当前时间距离1970-01-01 00:00:00的毫秒数 long start = System.currentTimeMillis(); //遍历100000以内的自然数 for(int i=2; i<100000; i++){ //让i除以j //优化二:两种优化方式:第一种j<i/2;第二种:Math.sqrt(i); //只对本身是质数有效 //临界值:除一个数等于这个数本身,即开方 for(int j=2; j<=Math.sqrt(i); j++){ if(i % j == 0){ isFlag = false; break;//优化一:只对本身非质数的自然数有效 } } if(isFlag){ sum++; } //重置isFlag isFlag = true; } long end = System.currentTimeMillis(); System.out.println("所花费的时间为:"+(end-start)+"毫秒"); System.out.println("质数个数为:"+sum); } }
方法二:利用带标签的continue实现 label:for(int i=2; i<=100000; i++){ for(int j=2; j<Math.sqrt(i); j++){ if(i%j == 0){ continue label; } } sum++; }
- 语法说明:
- 1.嵌套循环是指在一个循环语句的循环体中再定义一个循环语句的语法结构
- 2.一般两层循环的嵌套循环,外层循环用来控制行数,内层循环用来控制列数,外层循环执行一次,内层循环执行一遍;也可以嵌套多层
- 3.while,do…while,for循环语句都可以进行嵌套,并且它们之间也可以相互嵌套
5.跳转语句
public static void main(String[] args) { label:for(int i=1; i<5; i++){ for(int j=1; j<10; j++){ if(j%4 == 0){ break label; } System.out.print(j+","); } System.out.println(); } }
1.break语句
- 1.使用范围:
- switch-case
- 循环结构
- 2.swtich-case中的作用:
- 终止某个case并跳出switch语句
- 3.循环中的作用:
- 结束当前循环(如果是嵌套循环,只能结束当前层循环,如果想使用break语句跳出外层循环,则需要对外层循环添加标记)
- 4.带标签的break语句
- 结束指定标识的一层循环结构
- 5.注意:
- break关键字的(直接)后面不能声明执行语句,否则会编译错误(因为无法执行)
public static void main(String[] args) { label:for(int i=1; i<5; i++){ for(int j=1; j<10; j++){ if(j%4 == 0){ continue label; } System.out.print(j+","); } System.out.println(); } }
2.continue语句
- 1.使用范围:
- 循环结构
- 2.循环中的作用:
- 终止本次循环,执行下一次循环(嵌套循环语句中,continue语句后面也可以通过使用标记的方式结束本次外层循环,用法与break语句相似)
- 3.带标签的continue语句
- 结束指定标识的一层循环结构的当次循环
- 4.注意:
- continue关键字的(直接)后面不能声明执行语句,否则会编译错误(因为无法执行)
14.java中的数组
1.数组的定义
- 1.数组是内存中一块连续的存储空间,用来存储多个相同类型的数据
- 2.数组是一组数据的集合,数组中的每个数据被称作元素。数组可以存放任意类型的元素,但同一个数组里存放的元素类型必须一致
- 3.数组存储基本数据类型存储的是值,存储引用数据类型存储的是堆地址
2.数组的种类
- 1.按照维度:一维数组,二维数组,多维数组
- 2.按照元素的数据类型:基本数据类型元素的数组,引用数据类型元素的数组(即对象数组)
3.数组的相关概念
- 1.数据类型
- 2.数组名
- 3.数组元素
- 4.数组下标(索引,角标)
- 5.数组长度
4.数组的特点
- 1.数组是有序排列的
- 2.数组的长度一旦确定,就不能修改
- 3.可以直接通过下标(索引)的方式调用指定位置的元素
- 4.数组本身是引用数据类型,而数组中的元素既可以是基本数据类型,也可以是引用数据类型
- 5.创建数组对象会在内存中开辟一整块连续的空间,而数组名引用的是这块连续空间的首地址
- 6.如果创建数组时没有显性赋值,数组会根据相应的元素类型自动赋予默认值
1.一维数组
1.声明和初始化
//1.声明 数据类型[] 数组名; //2.1静态初始化:定义数组的同时为数组的每个元素赋值 数据类型[] 数组名 = new 数据类型[]{元素,元素。。。}; 数据类型[] 数组名 = {元素,元素。。。}; //2.1动态初始化:定义数组时只指定数组的长度,由系统赋初值(或显式赋值) 数据类型[] 数组名 = new 数据类型[数组长度];
- 说明:
- 1.不管是动态初始化还是静态初始化,数组一旦完成初始化,其长度就确定了,因为内存中要分配指定的长度的内存空间
- 2.数组的长度必须为整数
- 3.通过动态初始化的方式创建数组时必须指定长度,否则无法分配空间,而通过静态初始化的方式创建数组可以不指明长度,长度由{}的元素个数决定,如果没有元素,则为空数组,即长度为0
- 4.注意当使用
数据类型[] 数组名 = new 数据类型[]{元素,元素。。。};
不能再指定数组的长度,否则会报错,因为编译器会认为数组指定的元素个数与实际存储的元素个数有可能不一致,存在一定的安全隐患
2.访问数组元素
int[] arr = new int[]{5,6,7}; System.out.println(arr[2]);
- 说明:
- 1.可以通过数组下标的方式访问数组中的元素
- 2.数组的下标都有一个范围,即0~数组长度-1;访问数组的元素时,下标不能超出这个范围,否则会报数组下标越界异常
3.数组的长度
属性:length 数组名.length
4.数组遍历
int[] arr = new int[]{5,6,7}; for(int i=0; i<arr.length; i++){ System.out.println(arr[i]); }
- 说明:
- 1.依次获取数组中每个元素的过程称为数组遍历
5.默认初始化值
- 1.整型:默认初始化值0
- 2.浮点型:默认初始化值0.0
- 3.char: 默认初始化值0或’\u0000’,而非’0’, 效果像空格,但并不是空格,空格也有对应的asill值
- 4.boolean:默认初始化值是 false
- 5.引用类型:默认初始值是
null
6.数组的内存解析
- 1.栈中存放数组引用,该引用并非实际的数据,而是保存指向数组首元素的地址
- 2.堆中存放的才是数组中的数据
- 3.引用类型之间,相互赋值传递的是堆地址
- 4.堆地址寻址公式:数组首元素地址+下标*数组数据类型对应的字节长度
7.数组扩容
- 1.创建一个长度更大的数组,通常扩大两倍
- 2.把原数组中的数据复制到新数组中(循环)
- 3.把原数组的堆地址指向新数组的堆地址
- 扩容方式(三种)
//1.for循环扩容 int[] arr = {1,2,3,4,5}; int[] arr2 = new int[arr.length*2]; for(int i=0; i<arr.length; i++){ arr2[i] = arr[i]; } arr = arr2; System.out.println(Arrays.toString(arr)); //2.利用System.arraycopy扩容 int[] arr = {1,2,3,4,5}; int[] arr2 = new int[arr.length*2]; System.arraycopy(arr,0,arr2,0,arr.length); arr = arr2; System.out.println(Arrays.toString(arr)); //3.利用Arrays.copyOf扩容 int[] arr = {1,2,3,4,5}; arr = Arrays.copyOf(arr,arr.length*2); System.out.println(Arrays.toString(arr));
8.可变长参数数组
- 1.方法中长度可变的参数,实际长度由调用者传入的实参个数决定
- 2.一个方法中只能定义一个0-1个可变长参数,如果多于一个,则无法区分两个可变长参数的边界值
- 3.如果参数列表中存在多个参数,可变长参数必须写在最后一位
访问修饰符 返回值类型 函数名(数据类型...参数名){ 方法体 }
2.二维数组
1.二维数组的声明和初始化
- 1.一个一维数组(高纬度)中的元素还是一个一维数组(低维度)
- 2.创建方式:
//1.先声明再指明数组长度 数据类型[][] 数组名; 数组名 = new 数据类型[高维度数组长度][低纬度数组长度]; //2.声明的同时直接指明数组长度 数据类型[][] 数组名 = new 数据类型[高维度数组长度][低纬度数组长度] //3.创建的同时直接赋值 数据类型[][] 数组名 = {{值1,值2},{值3,值4},{值5,值6}...}; //4.不规则创建 数据类型[][] 数组名 = new 数据类型[高纬度数组长度][]; 数组名[高纬度数组下标] = new 数据类型[低纬度数组长度];
- 说明:
- 1.第一种方式元素个数=高纬度数组长度*低纬度数组长度
- 2.第三种方式外层{}代表高纬度数组,内层{}代表低纬度数组;高维数组长度由内层{}个数决定,低维数组长度由内层{}中的元素个数决定
- 3.第四种创建方式元素个数=所有低纬度数组长度之和
2.维数组的调用
- 1.数组名[高维度下标]:返回高维下标对应的低维度数组
- 2.数组名[高纬度下标][低纬度下标]:返回高纬度下标对应的低纬度数组中下标所对应的元素
3.二维数组的遍历
- 1.结合双层嵌套循环
- 2.外层循环循环高纬度数组
- 3.内层循环循环低纬度数组
- 4.在内层循环操作数组元素
- 5.通过数组名.length获取高纬度数组长度
- 6.通过数组名[高纬度数组下标].length获取低纬度数组长度
for(int i=0; i<数组名.length; i++){ for(int j=0; j<数组名[i].length; j++){ 循环体; } }