第三章:变量和运算符的世界
在本章中,你会学习到关于变量和运算符的一切。关于如何命名、声明、初始化一个变量以及对变量进行的一些常规操作。
3.1 什么是变量?
变量是在给我们程序中用来存储和操作的数据起的名字。举个例子,假如你的程序需要存储用户的年纪。我们可以命名这个数据为userAge 然后用如下的代码声明userAge 变量:
int userAge;
该声明语句首先声明变量的数据类型,然后是它的名字。
变量的数据类型指的是变量将以何种形式存储(比如它是一个数字或者是一个文本)。在我们的例子里,数据类型是int,代表整数类型(integers)。变量的名字是userAge。
3.2 Java的基本数据类型
Java中提前定义了八种数据类型。
前四种是整数类型:
byte
byte用来存储-128到127的整数,它使用一字节的存储空间。我们通常在存储空间很小或者我们确认这个变量不会超过-128到127的范围。
比如,我们可以用来存储用户的年龄,因为用户年龄基本不可能超过127岁。
short
short使用2byte的存储空间,范围是-32768到32767。
int
int使用4byte的存储空间,范围是-2147483648到2147483647。这是最常被用来存储整数的数据类型,而且它有最实用的范围。
long
long使用8byte的存储空间,范围是-263到263-1。这个很少被用到,除非你真的需要存储如此巨大的一个整数(比如地球的年龄)。为了声明long的值,你需要在数字后面添加"L"后缀。
除了存储整数的类型,我们还有存储浮点数(小数)的类型。它们是:
float
float使用4byte的存储空间,范围是-3.40282347 x 1038到+3.40282347 x 1038。它有7位的精度。这意味着如果你用浮点存储一个数比如1.23456789,它会被自动转换成7位(1.234568)。
double
double使用8byte的存储空间,范围是-1.79769313486231570 x 10308到1.79769313486231570 x 10308,有15位的精度。
通常来说,无论何时在Java中指定浮点数,它都自动认为是double而不是float。如果你需要Java将浮点数识别为float,你需要在数字后面加上“F”。
除非存储空间很小,否则你总是应该用double因为它有更高的精度。
除了上面提到的六种树脂类型,Java还有两种基本数据类型。它们是:
char
char代表字符(character)用来存储单个的Unicode字符比如“A”、“%”、“@”、“p”等,它使用2byte的存储空间。
boolean
boolean(布尔值)是一种特殊的基本类型,它只能有两种值:true和false。它通常用用在条件选择里,我们会在后面涉及到。
3.3 命名一个变量
Java的变量名只能使用字母、数字、下划线(_)或者美元符号($)。但是第一个字符不能是数字。因此,你可以命名你的变量为_userName, $username, username 或者userName2,但不能是2userName。
但是我们通常约定变量名总是以字母开头而不是“$”或者"_"。此外,美元符号基本从来不会用在变量命名上(尽管技术上使用没有错)。
变量名应该简短但有意义,旨在提示读者它的用处。用userName、userAge和userNumber而不是n、a和un来命名变量会更有意义。
除此之外,还有一些保留字是不能用来命名变量的因为它们已经提前在Java中定义了。这些保留字包括System、if、while等等,我们将会在后面学习到更多。
在Java通常实践中我们会使用驼峰命名法来命名变量。驼峰命名是混合大写复合词的做法,每个单词的首字母大写(比如thisIsAVariableName)。我们在之后都会使用这种方法。
最后,变量名是大小写敏感的。thisIsAVariableName 和thisisavariablename是不一样的。
3.4 变量初始化
每次你声明一个变量,你都需要给它一个初始值。这被称为变量初始化。你可以在之后的代码里改变变量的值。
有两种方法可以初始化变量。你可以在声明变量的时候就初始化或者在单独的语句中初始化它。
下面的代码演示了你可以在声明变量的时候初始化:
byte userAge = 20;
short numberOfStudents = 45;
int numberOfEmployees = 500;
long numberOfInhabitants = 21021313012678L;
float hourlyRate = 60. 5F;
double numberOfHours = 5120. 5;
char grade = 'A' ;
boolean promote = true;
byte level = 2, userExperience = 5;
如之前所说,当我们声明一个long的变量时,需要在最后加上“L”,不然编译器会报错因为这个数字太大了。此外,当我们在第5行声明float时,需要加上“F”后缀,不然编译器就会默认位double类型。
最后,在声明char类型时,我们需要用单引号’'包裹字符。
在第9行,我们演示了如何在同一行里声明两个变量,这两个变量通过一个逗号隔开,变量类型只需要声明一次。
当然,你也可以选择声明和初始化在两行语句中实现:
byte year; //先声明变量
year = 20; //之后初始化
3.5 赋值运算符
在程序中的等号"=“和数学中的等号”=“含义是不一样的。在程序中,等号”=“的意思是一个赋值运算符。意味着我们把等号”="右边的值赋给左边的变量。
在程序代码中,x=y和y=x的含义是大相径庭的。
迷糊了吗?我们来看个例子:
假如我们有两个变量x和y,
int x = 5;
int y = 10;
如果你写下
x=y;
这个语句意味着我们将y的值赋值给x,将一个变量的值赋给另一个变量是完全没问题的。现在x等于10同时y等于10。
3.6 基本运算符
除了给变量赋值外,我们还可以对它们进行基本的数学运算。Java的基本运算符包括+,-,*,/以及%。分别代表加减乘除以及取余数。
举例
假如x=7,y=2
加:x+y=9
减:x-y=5
乘:x*y=14
除:x/y=3(取整数,忽略余数)
取余数:x%y=1
在Java中,两个整数相除只会得到整数,如果其中有一个或两个不是整数,那么相除就会得到不是整数的结果。
比如
7/2=3
7.0/2=3.5
7/2.0=3.5
7.0/2.0=3.5
注意在Java中,7.0和7是不一样的,前者是浮点数,后者是整数。
3.7 更多的赋值运算
除了等号之外,在Java中还有几种赋值运算符(大部分编程语言中也有)。比如+=,-=,*=。
假设我们有一个变量x,初始值为10。如果我们需要让x的值加2,我们可以这么写:
x = x+2;
程序首先会算右边x+2,然后将值赋给左边,最后x就会等于12.
除此之外,我们也可以写成
x+=2;
可以表达同样的意思,因此x += 2等同于x = x + 2.
同样,我们也可以这样使用-=和*=.
大多数程序语言中同样有++和–运算符,当你想要变量的值+1时,就可以使用++运算符,比如:
int x=2;
x++;
x就会变成3,x++等同于x=x+1。
++可以放在变量名前或者变量名后,这会影响哪个任务先执行。
假如我们有一个整数变量counter,如果我们这么写
System.out.println(counter++);
程序会先打出counter的原有值再执行+1的运算:
System.out.println(counter);
counter = counter + 1;
如果我们这么写
System.out.println(++counter);
那么程序会先将counter的值+1,再将它+1后的值打印出来。
换句话说是这样执行
counter = counter + 1;
System.out.println(counter);
除了++外,还有–运算,每次-1,使用方法一致。
3.8 Java的类型转换
有时候在程序中,我们会需要将一个数值类型转换成另一个,比如从double转成int。这就是类型转换。
如果我们是从一个小的数据类型转换到大的数据类型,那不用我们做额外的工作。比如,下面的代码将short(2字节)转换成double(8字节)。这被称为扩大转换,不需要我们写额外的代码。
short age = 10;
double mydouble = age;
然而,如果我们要将大的数据类型转换成小的,我们就要用括号指出期望的数据类型。这被称为缩小转换,比如:
int x = (int) 20. 9;
在这里,我们将一个double(8字节)转换成int(4字节)。
缩小转换并不安全,应该尽量避免除非必须。因为缩小转换往往会导致数据丢失,当我们将20.9转换成整数时,会变成20,而不是21,因为小数部分会直接被舍弃。
除了做数值的类型转换,我们还可以转换别的类型,后文中将会继续探索这些。
第四章:数组(Array)和字符串(String)
在之前的章节中,我们涉及到了Java的八种常用类型。除了这八种数据类型,Java还包含了一些高级的数据类型。在本章我们将会涉及到其中两个:数组和字符串。
4.1 字符串(String)
首先,什么是字符串?字符串是一个文本片段,比如"Hello World"和"Good Morning".
为了声明和初始化一个String变量,你应该这么写:
String message = "Hello World";
message是这个变量的名字,“Hello World"是分配给它的字符串。注意你需要用”"将字符串包裹起来,要注意和中文的“”是不一样的。
你也可以将一个空的字符串赋值给变量比如:
String anotherMessage = "";
如果你想将多个字符串连起来,你可以用加号+连接,比如
String myName = "Hello World, " + "my name is Jamie";
等同于
String myName = "Hello World, my name is Jamie";
4.1.1 String的方法
不像我们之前学过的八种基本类型,String其实是一个对象。更具体的说它是String类里的一个对象。
暂时,你只需要知道String类提供给我们很多事先写好的方法,我们可以在程序中直接拿来用。方法是用来执行特定任务的可重复使用的代码块。我们稍后将会看到一些例子。
在Java中,方法会有不同的变化,下面讨论的例子中大部分的方法只是其中的一种变化。但是只要你学会一种,你就会使用相关的其他方法。现在让我们来看一些在String中常用的方法。
length()
length()方法告诉我们String字符串里字符的个数。
为了知道“Hello World”的长度,我们可以这么写
"Hello World".length();
每当我们使用一个方法时,我们应该用点“.”操作符,我们在点后面写出方法的名字,然后加上一对()。大部分方法完成任务时会返回一个值。length()方法返回String的长度。你可以将这个结果赋值给一个变量:
int myLength = "Hello World".length();
在这个例子中,myLength的值会是11,中间空格也算上。
你可以通过下面的语句展示出来:
int myLength = "Hello World".length();
System. out. println(myLength);
尝试将这两条语句写到第二章的HelloWorld.java里面,你需要将它们加入到main()方法的{}中间。运行这个程序,你会看到11作为输出结果。在下一章我们会讲到更多关于输出结果的内容。
toUpperCase()/toLowerCase()
这两个方法可以将字符串全部转换成大写字母或者全部转换成小写字母。
substring()
substring()方法是用来返回字符串的子字符串。Java的很多方法需要一个特定值才能工作。这个值就被称为参数。我们将参数放在方法名后面的括号中。substring()方法是一个需要一个参数才能工作的例子。
举个例子,为了从"Hello World"中截取字符串,我们可以使用如下方法:
String firstSubstring = "Hello World".substring(6);
在这条语句的右半部分,我们使用"Hello World"来调用substring()这个方法。括号里的6就是参数。这个参数是告诉编译器该从哪个位置开始截取。实质上,它要求编译器从位置(index)6开始截取字符串一直到最后。
需要注意在编程中,index是从0开始的而不是1.这几乎是在所有编程语言中的惯例。所以,在我们的例子中,“H"的index是0,而"W"的指针是6.这条语句将会截取出"World”,然后赋值给firstSubstring,所以firstSubstring的值为"World"。
substring()方法也有另一种变体来方便我们从一个位置截取到另一个。假如你要截取位置1到7,你可以这么写:
String message = "Hello World";
String secondSubstring = message.substring(1, 8);
在上面的例子中,我们先把"Hello World"赋值给message,然后使用message调用substring()方法。
这两个参数是1和8。
和之前一样,第一个参数告诉编译器开始截取的位置。第二个参数告诉编译器停止截取的位置。位置8是不包含在里面的,用数学上的集合表示就应该是[1,8),所以截取出来的字符串是"ello Wo".
secondSubstring就会变成"ello Wo",而message仍然是"Hello World"
charAt()
这个方法返回一个特定位置的字符,可以被赋值给一个char变量。
举例,语句是:
char myChar = "Hello World".charAt(1);
截取到位置为1的字符然后赋值给myChar,因此,myChar的值为"e".
equals()
equals()方法是用来比较两个字符串是否一致。如果一致就会返回true否则返回false.
比如
boolean equalsOrNot = "This is Jamie".equals("This is Jamie");
boolean equalsOrNot2 = "This is Jamie".equals("Hello World");
equalsOrNot会是true,equalsOrNot2会是false。
split()
split()用于将一个字符串通过用户自定义的分隔符分成数个子字符串。分割完字符串之后,返回一个包含子字符串的数组。数组是相关数据的一个集合,我们将会在下一章讲到。
假如你要截取"Peter, John, Andy, David",你可以这么做:
String names = "Peter, John, Andy, David";
String[] splitNames = names.split(", ");
在这里,我们首先将需要截取的字符串赋值给变量names。然后我们用names调用
split()方法。这个方法需要一个参数——这个字符串的分隔符,在这里我们选择的分隔符是一个逗号加空格", "。
这个例子的结果是{“Peter”, “John”, “Andy”, “David”},这个数组被赋值给splitNames.
以上涉及了一些Java里常用的String的方法,如果想要查看更多可以到官网查看文档https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#method.summa
4.2 数组(Array)
现在,让我们看下数组。
数组是一个通常互相有关的数据的集合。假如我们要存储5个用户的年龄。我们可以将它们存储为一个数组而不是user1Age, user2Age, user3Age, user4Age 和user5Age。
声明一个数组变量有两种方法。第一种是如下:
int[] userAge;
int表明这个变量存储int数据,
[]表明这是一个数组而不是普通变量,
userAge是这个变量的名字。
第二种方法是如下:
int userAge[];
这种方法来自于C/C++ 语言然后被Java采用以便让C/C++程序员适应。但是这不是个Java推荐的语法,所以在这里我们仍然使用第一种方法。
在你声明一个数组变量之后,你需要创建一个数组然后赋值给这个数组变量。我们使用关键字new来创建:
int[] userAge;
userAge = new int[]{21, 22, 23, 24, 25};
一旦你初始化了一个数组,这个数组的大小就不能再改变,在这个例子里,userAge这个数组只能存储5个整数,因为初始化的时候就是5个。目前userAge数组的值是{21, 22, 23, 24, 25}。
除了用两行语句声明数组外,我们还可以用一行代码解决:
int[] userAge2 = new int[]{21, 22, 23, 24, 25};
也可以进一步简化为:
int[] userAge2 = {21, 22, 23, 24, 25};
第三种方法是:
int[] userAge3 = new int[5];
这行语句新建了一个5位数组,因为没有特别指定数值,Java会自动生成默认的值,然后赋值给userAge3。默认的值是0,所以userAge3是{0,0,0,0,0}.
你可以用指针来访问数组中的元素并修改它们,起始位置是0,后面依次是1~4.
比如userAge[0]=31,那么数组就会变成{31, 22, 23, 24, 25}.
如果我们写userAge[2]=userAge[2]+20,那么就会变成{31, 22, 43, 24, 25}.
4.2.1 数组的方法
就像String,数组同样也有一系列预先写好的方法。
我们下面讨论的方法在java. util. Arrays类里,为了使用它们,你需要在程序顶部加入
import java.util.Arrays;
这会告诉编译器去哪找到这些方法。
import语句一定要在package后面,类声明前面,例如:
package helloworld;
import java.util.Arrays;
public class HelloWorld {
//Code for HelloWorld class
}
如果还记得的话我们之前用过String类,但是并没有写任何import语句。这是因为String类是包含在java.lang包里的,而Java默认就会引入这个包。
现在,让我们看看数组的常用方法。
equals()
equals()用来比较两个数组是否相等,如果两个数组拥有相同的数值以及相同的排列顺序,两个数组就会被认为是相等的,返回true。
示例:
int[] arr1 = {0,2,4,6,8,10};
int[] arr2 = {0,2,4,6,8,10};
int[] arr3 = {10,8,6,4,2,0};
boolean result1 = Arrays.equals(arr1, arr2);
boolean result2 = Arrays.equals(arr1, arr3);
result1会是true,result2会是false。
注意到在上面的例子中,我们在方法名前面加入了Arrays这个词。这是因为Arrays中所有的方法都是静态(static)的。为了调用一个静态方法,你必须将类名加在前面。
toString()
toString()方法返回一个代表数组内容的String字符串。这让我们方便的展示数组的内容。比如你有一个数组int[] numbers = {1, 2, 3, 4, 5};
你可以用如下方法展现数组内容:
System.out.println(Arrays.toString(numbers));
就会得到[1, 2, 3, 4, 5]作为输出结果。
需要更多的数组方法参考https://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html
4.2.2 找到数组的长度
int [] userAge = {21,22,26,32,40}
userAge. length是5,这就是数组的长度,不用在lenght后面加括号。
4.3 基本类型🆚包装类型
既然我们已经熟悉了String和Array,现在让我们来讨论下Java中的一个重要概念,关于数据类型。
在Java中所有的数据类型都能被划分为基本类型和包装类型。在Java中只有8中基本类型(byte,short,long,int,double,float,char和boolean),剩下的都是包装类型。
基本类型和包装类型的主要差异是它们存储数据的方式。
基本类型存储它本身的数据。
当我们写下:
int myNumber = 5;
myNumber变量存储的数据就是5.
而包装类型,并不存储实际的数值,而是存储一个数值的引用。它不是告诉编译器这个值具体是多少,而是告诉它去哪找到这个实际的数值。
包装类型的一个例子就是String,当你写下一条语句比如:
String message = "Hello";
message变量实际上存储的不是"Hello","Hello"这个字符串被创建然后存储在计算机内存的一个地方。message变量存储的是一个内存地址。
对于初学者,现在我们只需要知道这么多,没必要去更深入的知道相关的细节。只是要注意基本类型和包装类型的不同之处,前者存储的是真实的值而后者保存的是一个引用。
4.4 字符串是不可变的
不可变意味着String的值是不能再改变的。当我们更新一个String变量时,我们实际上是创建了一个新的String变量然后将内存地址赋值给String变量。比如:
String message = "Hello";
我们知道编译器会创建"Hello"的字符串然后将它保存在内存的某处。message这个变量保存的是这个内存地址。现在如果我们将这个词变为"World":
message = "World";
编译器实际上不是去"Hello"保存的地方将它变为"World",而是新建一个 "World"字符串将它保存在内存里,新的地址赋值给message。换句话说,现在内存里有两个字符串——“Hello"和"World”。如果"Hello"在程序中不会再被用到,它就会被销毁然后释放出内存空间,这个过程被称为垃圾回收机制(Garbage Collection)而且是被Java自动处理的。