程序结构
首先我们来看一下java程序
/**
*可以自动创建注释
*/
public class Hello{
public static void main(String[] agrs){
System.out.println("Hello World!");//执行码
}
/*多行注释
多行注释开始
多行注释
多行注释结束 */
}//class定义结束
java程序是面向对象的语言,所以一个程序的基本单位就是class
,class
是关键字,上诉程序的class
的命名为Hello
:
public class Hello{//Hello是类名,public是对类名的修饰
}
类名的命名规则:
1.类名必须以字母开头,后接字母,数字和下划线结合
2.习惯以大写字母开头
在class
的内部有众多的方法,如下
public static void main(String[] agrs){
}/*
其中public和static是对方法的修饰
void代表的方法的返回值为空
方法内的String[]是定义方法内的参数
*/
方法也有命名规则,命名和class
一样也是以字母开头,习惯是以小字母开头后接字母,数字和下划线结合。
在方法的内部语句才是真正的执行码,java
的每一句以分号结束
public class Hello{
public static void main(String[] args){
System.out.println ("Hello!");
}
}
注释:
这是单行注释
//单行注释
这是多行注释
/*
*/
特殊的多行注释
/**
*/
变量和数据类型
变量就是一个未知的量可以变化量,和我们在中学时代学习的变量一样,例如y=x+1
其中y
和x
就是变量。
在java
中定义了两种变量:基本类型变量和引用类型变量。
变量
首先我们来讨论基本类型变量,在java中变量必须先定义才能被使用,在使用变量时得先给它一个初始值,如:
int x=1;
这里int
是对变量x的定义,本次定义的变量x
为int
型,初始值为1
。不写初始值的话就相当于给了变量一个默认值,默认值为0
。
下面来看一个完整的定义变量,然后打印变量
public class Test {
public static void main(String[] args) {
int x=100;//对变量x的定义
System.out.println(x);//打印变量x
}
}
运行的结果
变量还有一个重要的特性就是可以重新赋值,例如先给变量x
赋值100
,后给变量赋值200
public class Test {
public static void main(String[] args) {
int x=100;//对变量x的定义
System.out.println(x);//打印变量x
x=200;//再次对变量赋值,为200
System.out.println(x);//打印变量
}
}
运行结果如下
可以看到第一次对变量赋值时已经对变量定义了,所以第二次对变量赋值时不需要对变量再次定义。
变量不仅可以重新赋值,还可以赋值给其他变量,如下代码:
public class Test {
public static void main(String[] args) {
int x=100;//对变量x的定义
System.out.println("x ="+x);//打印变量x
int y=x;//将变量x赋值给y,观察y输出是否与变量x一致
System.out.println("y ="+y);//打印变量
}
}
运行结果如下:
运行之后可以看出变量x的值与变量y值是一样的。
由此可以看出,变量可以反复赋值,这里需要提醒一下,这里的“=”与数学中的概念不同,这是一个赋值的意思。
基本数据类型
java
定义了一下几种数据类型
整数类型:byte,short,int,long
浮点数类型:float,double
字符类型:char
布尔类型:boolean
java
定义的这几类数据类型有什么区别呢?这个根据计算机的存储这些类型所占的字节数来区分的,大家都知道计算机的底层数据是以二进制这种形式存储的,而计算机最小存储单位为字节byte
,而一个字节就是一个8
位的二进制数,即8
个bit
。它的二进制表示范围是000 000 00~111 111 11
,换成10
进制就是0
到255
,16
进制就是00
到ff
。
一个字节是1byte
,1024
字节是1K
,1024K
是1M
,1024M
是1G
,1024G
是1T
。
不同的数据类型占用的字节数不一样。我们看一下Java
基本数据类型占用的字节数:
byte
恰好就是一个字节,而long
和double
需要8
个字节。
整形
因为在计算机中第一个bit
表示符号(0
表示正,1
表示负),各种类型的大致范围如下:
byte:-128~127
short: -32768 ~ 32767
int:-2147483648 ~ 2147483647
long: -9223372036854775808 ~ 9223372036854775807
我们来看一下定义的例子:
public class Test {
public static void main(String[] args){
byte i=-128;
short i1=32767;
int i2=-2147483648;
long i3=9000000000000000000L;
System.out.println("i="+i);
System.out.println("i1="+i1);
System.out.println("i2="+i2);
System.out.println("i3="+i3);
}
}
浮点型
浮点型数据就是小数,通常是用科学记数法表示的,所以小数点可以浮动,如1234.5
可以表示成12.345*10^2
也可以表示成1.2345*10^3
,所以称为浮点数。
下面是浮点数的定义:
public class Folat {
public static void main(String[] args) {
float f1=3.14f;
float f2=3.14e38f;//科学记数法表示3.14*10^38
double d1=1.97e307;
double d2=-1.79e308;//科学记数法表示-1.79*10^308
System.out.println("f1 ="+f1);
System.out.println("f2 ="+f2);
System.out.println("d1 ="+d1);
System.out.println("d2 ="+d2);
}
}
对于浮点型数据后面要加上f
,最大可以表示10^38
,而double
最大可以表示
10^308
。
布尔类型
布尔类型只有true
和false
两个值,布尔类型总是关系到运算的结果。
定义如下:
public class Boolean {
public static void main(String[] args) {
boolean b1=true;
boolean b2=false;
boolean isGerate=5>2;//5大于2,则运行的结果就为true
boolean beGerate= 6<1;//6大于1,则运行结果就为false
System.out.println("b1 ="+b1);
System.out.println("b2 ="+b2);
System.out.println("isGerate ="+isGerate);
System.out.println("beGerate ="+beGerate);
}
}
运行结果
字符类型
java
中还定义了字符类型的数据char
,就表示一个字符。Java
的char
类型除了可表示标准的ASCII
外,还可以表示一个Unicode
字符:
定义如下
public class Char {
public static void main(String[] args) {
char z='中';
char g='国';
char w='我';
char a='爱';
char n='你';
System.out.println(z);
System.out.println(g);
System.out.println(w);
System.out.println(a);
System.out.println(n);
}
}
运行结果如下
引用类型
除了上述基本类型的变量,剩下的都是引用类型。例如,引用类型最常用的就是String
字符串:
public class Test {
public static void main(String[] args){
String s="Hello";
System.out.println(s);
}
}
常量
定义常量是需要在前面加上final
定义如下:
public class Test {
public static void main(String[] args){
final double pi=3.14;//pi是一个常量
double r=2;
double area=r*r*pi;
pi=300;//显示错误
System.out.println(area);
}
}
变量的作用范围
在Java
中,多行语句用{ }
括起来。很多控制语句,例如条件判断和循环,都以{ }
作为它们自身的范围,只要正确地嵌套这些{ }
,编译器就能识别出语句块的开始和结束。
而在语句块中定义的变量,它有一个作用域,就是从定义处开始,到语句块结束。
超出了作用域引用这些变量,编译器会报错。
定义变量时,要遵循作用域最小化原则,尽量将变量定义在尽可能小的作用域,并且,不要重复使用变量名。
字符和字符串
在java
中字符和字符串是两种不同的数据类型。
字符
字符类型char
是基本数据类型,它是character
的缩写,也可以用Unicode
来表示,一个字符可以保存一个Unicode
字符。
char c='a';
char c1='中';
因为Java
在内存中总是使用Unicode
表示字符,所以,一个英文字符和一个中文字符都用一个char
类型表示,它们都占用两个字节。要显示一个字符的Unicode
编码,只需将char
类型直接赋值给int
类型即可:
int n1 = 'a'; // 字母“A”的Unicodde编码是97
int n2 = '中'; // 汉字“中”的Unicode编码是20013
也可以直接用Unicode
来表示一个字符,需要加上转义/u;
char n='\u0061';//Unicode中是16进制保存数据的,0061代表的就是97
char n1='\u4e2d';//Unicode中是16进制保存数据的,4e2d代表的就是20013
字符串
与字符char
不同的是,字符串是引用类型数据,一个字符串可以存储0
个到多个字符,我们用"...."
表示。
String a="";//包含零个字符
String b="a";//包含一个字符
String c="abc";//包含三个字符
String d="abc 789";//包含七个字符,其中一个为空格
因为字符串使用双引号"..."
表示开始和结束,那如果字符串本身恰好包含一个"
字符怎么表示?例如,"abc"xyz"
,编译器就无法判断中间的引号究竟是字符串的一部分还是表示字符串结束。这个时候,我们需要借助转义字符\
:
String s = "abc\"xyz"; // 包含7个字符: a, b, c, ", x, y, z
常见的转义字符包括:
\" 表示字符"
\' 表示字符'
\\ 表示字符\
\n 表示换行符
\r 表示回车符
\t 表示Tab
\u#### 表示一个Unicode编码的字符
字符串的连接
Java
的编译器对字符串做了特殊照顾,可以使用+
连接任意字符串和其他数据类型,这样极大地方便了字符串的处理。例如:
String s="hello";
String s1="world";
String s2="!";
String s3=s+s1+s2;
如果用+
连接字符串和其他数据类型,会将其他数据类型先自动转型为字符串,再连接:
int age=23;
String s="age is ";
String s3=s+age;
多行字符串
从Java 13
开始,字符串可以用"""..."""
表示多行字符串(Text Blocks)
了。举个例子:
String s="""
我是中国人
我爱我的祖国\n""";
不可变特性
public class S {
public static void main(String[] args) {
String s="hello";
System.out.println(s);//显示hello
s="world";
System.out.println(s);//显示world
}
}
空值null
引用类型的变量可以指向一个空值null,它表示不存在,即该变量不指向任何对象。
注意要区分空值null
和空字符串""
,空字符串是一个有效的字符串对象,它不等于null
。
数组类型
如果我们有一组相同变量,比如一个小组成员的年龄,代码如下:
public class Group {
public static void main(String[] args) {
//五个成员的年龄
int age1=23;
int age2=24;
int age3=26;
int age4=30;
int age5=40;
}
}
这样很麻烦,可以引入数组来解决这个麻烦的问题:
public class Group {
public static void main(String[] args) {
//五个成员的年龄
int[] age=new int[5];
age[0]=23;
age[1]=24;
age[2]=26;
age[3]=30;
age[4]=40;
for(int i=0;i<age.length;i++)
{
System.out.println(age[i]);
}
}
}
定义一个数组,必须使用"类型[]"
,和单个基本类型变量不同,数组变量初始化必须使用new int[5]
表示创建一个可容纳5
个int
元素的数组。
Java
的数组有几个特点:
数组所有元素初始化为默认值,整型都是0
,浮点型是0.0
,布尔型是false
;
数组一旦创建后,大小就不可改变。
要访问数组中的某一个元素,需要使用索引。数组索引从0
开始,例如,5
个元素的数组,索引范围是0~4
。
可以用数组变量.length
获取数组大小:
public class Group {
public static void main(String[] args) {
//五个成员的年龄
int[] age=new int[5];
age[0]=23;
age[1]=24;
age[2]=26;
age[3]=30;
age[4]=40;
for(int i=0;i<age.length;i++)
{
System.out.println(age[i]);
System.out.println(age.length);
}
}
}
数组是引用类型,在使用索引访问数组元素时,如果索引超出范围,运行时将报错:
public class Group {
public static void main(String[] args) {
//五个成员的年龄
int[] age=new int[5];
age[0]=23;
age[1]=24;
age[2]=26;
age[3]=30;
age[4]=40;
age[5]=50;
for(int i=0;i<age.length;i++)
{
System.out.println(age[i]);
System.out.println(age.length);
}
}
}
也可以在定义数组时直接指定初始化的元素,这样就不必写出数组大小,而是由编译器自动推算数组大小。例如:
public class Group {
public static void main(String[] args) {
//五个成员的年龄
int[] age=new int[]{23,24,26,30,40};
for(int i=0;i<age.length;i++)
{
System.out.println(age[i]);
System.out.println(age.length);
}
}
}
可以进一步简写为
int[] age={23,24,26,30,40};
字符串数组:
public class Group {
public static void main(String[] args) {
//五个成员的年龄
String []names= {"张三","李四","王五","赵六","武七"};
for(String b:names)
System.out.println(b);
}
}
整数运算
java
整数运算规则遵循数学上的四则运算规则,可以使用小括号任意嵌套,如下:
public class Main {
public static void main(String[] args) {
int i=1+2;
int j=9-5;
int k=6*9;
int m=36/6;
int s=(4+5)*(6-3)/3;
System.out.println(i);
System.out.println(j);
System.out.println(k);
System.out.println(m);
System.out.println(s);
}
}
整数的数组不但精确,就连整数运算得到的结果也是精确的,比如除法计算的是整数部分:
int m=11/2;//结果为5,而不是5.5
使用求余%,求的结果为1:
int m=11%2;//使用求余,求的余数为1
特别注意:整数的除法对于除数为0
时运行时将报错,但编译不会报错。
溢出
因为整数的表示范围有限,所以当运算的范围超出了整数的范围,不会报错会出现奇怪的结果,这是由于计算机的存储方式决定的。
还有一种简写的运算符,即+=,-=,*=,/=,
它们的使用方法如下:
n += 100; // 3409, 相当于 n = n + 100;
n -= 100; // 3309, 相当于 n = n - 100;
自增自减
Java
还提供了++
运算和--
运算,它们可以对一个整数进行加1
和减1
的操作:
public class Main {
public static void main(String[] args) {
int i=600;
i=i++;//相当于i=i+1
i=i--;//相当于i=i-1
System.out.println(i);
}
}
注意++
写在前面和后面计算结果是不同的,++n
表示先加1
再引用n
,n++
表示先引用n
再加1
。不建议把++
运算混入到常规运算中,容易自己把自己搞懵了。
移位运算
在计算中整数是以二进制存储的,所以可以进行位运算,例如整数8,
计算机中是这样存储的 00000000 00000000 00000000 00001000
整数是四个字节。可以对整数进行位运算:
public class Main {
public static void main(String[] args) {
int n=8;//00000000 00000000 00000000 00001000
int a=n<<1;//00000000 00000000 00000000 00010000,16
int b=n<<2;//00000000 00000000 00000000 00100000,32
int c=n>>1;//00000000 00000000 00000000 00000100,4
int d=n>>2;//00000000 00000000 00000000 00000010,2
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
}
}
如果对一个负数进行右移,最高位的1
不动,结果仍然是一个负数:
int n = -536870912;
int a = n >> 1; // 11110000 00000000 00000000 00000000 = -268435456
int b = n >> 2; // 11111000 00000000 00000000 00000000 = -134217728
int c = n >> 28; // 11111111 11111111 11111111 11111110 = -2
int d = n >> 29; // 11111111 11111111 11111111 11111111 = -1
还有一种无符号的右移运算,使用>>>
,它的特点是不管符号位,右移后高位总是补0
,因此,对一个负数进行>>>
右移,它会变成正数,原因是最高位的1
变成了0
:
int n = -536870912;
int a = n >>> 1; // 01110000 00000000 00000000 00000000 = 1879048192
int b = n >>> 2; // 00111000 00000000 00000000 00000000 = 939524096
int c = n >>> 29; // 00000000 00000000 00000000 00000111 = 7
int d = n >>> 31; // 00000000 00000000 00000000 00000001 = 1
对byte
和short
类型进行移位时,会首先转换为int
再进行位移。
仔细观察可发现,左移实际上就是不断地×2
,右移实际上就是不断地÷2
。
位运算
位运算是按位进行与、或、非和异或的运算。
与运算的规则是,必须两个数同时为1
,结果才为1
:
n = 0 & 0; // 0
n = 0 & 1; // 0
n = 1 & 0; // 0
n = 1 & 1; // 1
或运算规则是,是两个数任意一个数1
,结果为1
:
n = 0 | 0; // 0
n = 0 |1; // 1
n = 1 |0; // 1
n = 1 |1; // 1
非运算的规则是,0
和1
互换:
n = ~0; // 1
n = ~1; // 0
异或运算的规则是,如果两个数不同,结果为1
,否则为0
:
n = 0 ^ 0; // 0
n = 0 ^ 1; // 1
n = 1 ^ 0; // 1
n = 1 ^ 1; // 0
运算优先等级
在Java
的计算表达式中,运算优先级从高到低依次是:
()
! ~ ++ --
× / %
+-
<< >> >>>
&
|
+= -= *= /=
类型自动提升与强制转型,在运算过程中,如果参与运算的两个数类型不一致,那么计算结果为较大类型的整型。也可以将结果强制转型,即将大范围的整数转型为小范围的整数。强制转型使用(类型)
,例如,将int
强制转型为short
:
int i = 12345;
short s = (short) i; // 12345
浮点数运算
浮点数运算与整数运算相比只能进行加减乘除的数值运算,不能进行移位运算和位运算。在计算机中虽然浮点数的范围很大,但是浮点数有个特点,就是浮点数通常无法精确表示。
举个栗子:
浮点数0.1
在计算机中就无法精确表示,因为十进制的0.1
换算成二进制是一个无限循环小数,很显然,无论使用float
还是double
,都只能存储一个0.1
的近似值。但是,0.5
这个浮点数又可以精确地表示。
因为浮点数常常无法精确表示,因此,浮点数运算会产生误差:
public class Fc {
public static void main(String[] args) {
double x=1.0/10;
double y=1-9.0/10;
System.out.println(x);
System.out.println(y);
// 观察x与y的值是否相同,运算结果表示x=0.1,而y=0.09999998
}
}
由于浮点数存在运算误差,所以比较两个浮点数是否相等常常会出现错误的结果。正确的比较方法是判断两个浮点数之差的绝对值是否小于一个很小的数:
double r=Math.abs(x-y);
if(r<0.00001)
{
//两数相等
}
else
{
//两数不相等
}
浮点数在内存的表示方法和整数比更加复杂。Java
的浮点数完全遵循IEEE-754
标准,这也是绝大多数计算机平台都支持的浮点数标准表示方法。
类型提升
如果参与运算的的数中有一个是整数,可以自动提升为浮点数:
public class Fc {
public static void main(String[] args) {
int n=5;
double x=1.2+24.0/n;//6.0
System.out.println(x);
}
}
需要特别注意,在一个复杂的四则运算中,两个整数的运算不会出现自动提升的情况。例如:
int n=5;
int m=24;
double x=1.2+m/n;//5.2
溢出
整数运算在除数为0时会报错,而浮点数运算在除数为0时,不会报错,但会返回几个特殊值:
double x=0.0/0;
double y =1.0/0.0;
double z=-1.0/0.0;
System.out.println(x);//NaN表示Not a Number
System.out.println(y);//Infinity 表示无穷大
System.out.println(z);//-Infinity 表示无穷小
可以将浮点数强制转型为整数。在转型时,浮点数的小数部分会被丢掉。如果转型后超过了整型能表示的最大范围,将返回整型的最大值。例如:
int n1 = (int) 12.3; // 12
int n2 = (int) 12.7; // 12
int n2 = (int) -12.7; // -12
int n3 = (int) (12.7 + 0.5); // 13
int n4 = (int) 1.2e20; // 2147483647
如果要进行四舍五入,可以对浮点数加上0.5
再强制转型:
ublic class Main {
public static void main(String[] args) {
double d = 2.6;
int n = (int) (d + 0.5);
System.out.println(n);//n为3
}
}
布尔运算
对于boolean
运算,只有两种结果true
和false
。
布尔运算是一种关系运算,包括以下几种:
比较运算符:>,>=,<,<=,==,!=
与运算 &&
或运算 ||
非运算 !
关系运算符的优先级从高到低依次是:
!
>,>=,<,<=
==,!=
&&
||
短路运算
布尔运算有一个很重要的特性就是短路运算,如果一个运算可以提前确定结果则可以使用短路运算,不再进行后面的运算,直接返回结果。
因为false && x
的结果总是false
,无论x
是true
还是false
,因此,与运算在确定第一个值为false
后,不再继续计算,而是直接返回false
。
public class Duanlu {
public static void main(String[] args) {
boolean b=5<3;
boolean result =b&&(5/0>0);
System.out.println(result);
}
}
如果没有短路运算,&&
后面的表达式会由于除数为0
而报错,但实际上该语句并未报错,原因在于与运算是短路运算符,提前计算出了结果false
。
如果变量b
的值为true
,则表达式变为true && (5 / 0 > 0)
。因为无法进行短路运算,该表达式必定会由于除数为0
而报错,可以自行测试。
类似的,对于||
运算,只要能确定第一个值为true
,后续计算也不再进行,而是直接返回true
:
boolean result =b||(5>0);//true
三元运算符
Java
还提供一个三元运算符b ? x : y
,它根据第一个布尔表达式的结果,分别返回后续两个表达式之一的计算结果。示例:
ublic class Main {
public static void main(String[] args) {
int n = -100;
int x = n >= 0 ? n : -n;
System.out.println(x);
}
}
上述语句的意思是,判断n >= 0
是否成立,如果为true
,则返回n
,否则返回-n
。这实际上是一个求绝对值的表达式。
注意到三元运算b ? x : y
会首先计算b
,如果b
为true
,则只计算x
,否则,只计算y
。此外,x
和y
的类型必须相同,因为返回值不是boolean
,而是x
和y
之一。