一前言
由于我们的重点是数据结构与算法,所以我会快速的将JAVA 基础知识过一遍,而且有了c语言的基础JAVA是很好学的。
二主要内容
本节我们将介绍Java程序的基础知识,包括:
Java程序基本结构
我们先剖析一个完整的Java程序,它的基本结构是什么:
/** * 可以用来自动创建文档的注释 */ public class Hello { public static void main(String[] args) { // 向屏幕输出文本: System.out.println("Hello, world!"); /* 多行注释开始 注释内容 注释结束 */ } } // class定义结束因为Java是面向对象的语言,一个程序的基本单位就是class,class是关键字,这里定义的class名字就是Hello:
public class Hello { // 类名是Hello // ... } // class定义结束类名要求:
- 类名必须以英文字母开头,后接字母,数字和下划线的组合
- 习惯以大写字母开头(这里和c语言不一样但是答题一致)
要注意遵守命名习惯,好的类命名:
- Hello
- NoteBook
- VRPlayer
不好的类命名:
- hello
- Good123
- Note_Book
- _World
注意到public是访问修饰符,表示该class是公开的。
不写public,也能正确编译,但是这个类将无法从命令行执行。
在class内部,可以定义若干方法(method):(这个算是特有的了)
public class Hello { public static void main(String[] args) { // 方法名是main // 方法代码... } // 方法定义结束 }方法定义了一组执行语句,方法内部的代码将会被依次顺序执行。
这里的方法名是main,返回值是void,表示没有任何返回值。
我们注意到public除了可以修饰class外,也可以修饰方法。而关键字static是另一个修饰符,它表示静态方法,后面我们会讲解方法的类型,目前,我们只需要知道,Java入口程序规定的方法必须是静态方法,方法名必须为main,括号内的参数必须是String数组。
方法名也有命名规则,命名和class一样,但是首字母小写:
好的方法命名:
- main
- goodMorning
- playVR
不好的方法命名:
- Main
- good123
- good_morning
- _playVR
在方法内部,语句才是真正的执行代码。Java的每一行语句必须以分号结束:
public class Hello { public static void main(String[] args) { System.out.println("Hello, world!"); // 语句 } }在Java程序中,注释是一种给人阅读的文本,不是程序的一部分,所以编译器会自动忽略注释。
Java有3种注释,第一种是单行注释,以双斜线开头,直到这一行的结尾结束:
// 这是注释...而多行注释以/*星号开头,以*/结束,可以有多行:
/* 这是注释 blablabla... 这也是注释 */还有一种特殊的多行注释,以/**开头,以*/结束,如果有多行,每行通常以星号开头:
/** * 可以用来自动创建文档的注释 * * @auther liaoxuefeng */ public class Hello { public static void main(String[] args) { System.out.println("Hello, world!"); } }这种特殊的多行注释需要写在类和方法的定义处,可以用于自动创建文档。
Java程序对格式没有明确的要求,多几个空格或者回车不影响程序的正确性,但是我们要养成良好的编程习惯,注意遵守Java社区约定的编码格式。(最好还是按格式,因为看的很舒服,我们班有些初学者的代码看的我头疼)
具体的代码格式要求可以在Eclipse的设置中Java-Code Style查看
变量和数据类型
在Java中,变量分为两种:基本类型的变量和引用类型的变量。
我们先讨论基本类型的变量。
在Java中,变量必须先定义后使用,在定义变量的时候,可以给它一个初始值。例如:
int x = 1;上述语句定义了一个整型int类型的变量,名称为x,初始值为1。
不写初始值,就相当于给它指定了默认值。默认值总是0。
来看一个完整的定义变量,然后打印变量值的例子:
// 定义并打印变量 public class Main { public static void main(String[] args) { int x = 100; // 定义int类型变量x,并赋予初始值100 System.out.println(x); // 打印该变量的值 } }变量的一个重要特点是可以重新赋值。例如,对变量x,先赋值100,再赋值200,观察两次打印的结果:
// 重新赋值变量 public class Main { public static void main(String[] args) { int x = 100; // 定义int类型变量x,并赋予初始值100 System.out.println(x); // 打印该变量的值,观察是否为100 x = 200; // 重新赋值为200 System.out.println(x); // 打印该变量的值,观察是否为200 } }注意到第一次定义变量x的时候,需要指定变量类型int,因此使用语句int x = 100;。而第二次重新赋值的时候,变量x已经存在了,不能再重复定义,因此不能指定变量类型int,必须使用语句x = 200;。
变量不但可以重新赋值,还可以赋值给其他变量。让我们来看一个例子:
// 变量之间的赋值 public class Main { public static void main(String[] args) { int n = 100; // 定义变量n,同时赋值为100 System.out.println("n = " + n); // 打印n的值 n = 200; // 变量n赋值为200 System.out.println("n = " + n); // 打印n的值 int x = n; // 变量x赋值为n(n的值为200,因此赋值后x的值也是200) System.out.println("x = " + x); // 打印x的值 x = x + 100; // 变量x赋值为x+100(x的值为200,因此赋值后x的值是200+100=300) System.out.println("x = " + x); // 打印x的值 System.out.println("n = " + n); // 再次打印n的值,n应该是200还是300? }这和c语言都一样,我就精简一点
基本数据类型
基本数据类型是CPU可以直接进行运算的类型。Java定义了以下几种基本数据类型:(大部分和从都一样,如果没学过c语言的可以看我上一篇文字,有廖雪峰老师的原文档)
- 整数类型:byte,short,int,long
- 浮点数类型:float,double
- 字符类型:char
- 布尔类型:boolean
Java定义的这些基本数据类型有什么区别呢?要了解这些区别,我们就必须简单了解一下计算机内存的基本结构。
计算机内存的最小存储单元是字节(byte),一个字节就是一个8位二进制数,即8个bit。它的二进制表示范围从00000000~11111111,换算成十进制是0~255,换算成十六进制是00~ff。
内存单元从0开始编号,称为内存地址。每个内存单元可以看作一间房间,内存地址就是门牌号。
0 1 2 3 4 5 6 ... ┌───┬───┬───┬───┬───┬───┬───┐ │ │ │ │ │ │ │ │... └───┴───┴───┴───┴───┴───┴───┘我们在这里详解c语言没有的或者是不常见的。int,float我就不过多赘述了
布尔类型
布尔类型boolean只有true和false两个值,布尔类型总是关系运算的计算结果:
boolean b1 = true; boolean b2 = false; boolean isGreater = 5 > 3; // 计算结果为true int age = 12; boolean isAdult = age >= 18; // 计算结果为falseJava语言对布尔类型的存储并没有做规定,因为理论上存储布尔类型只需要1 bit,但是通常JVM内部会把boolean表示为4字节整数。
引用类型
除了上述基本类型的变量,剩下的都是引用类型。例如,引用类型最常用的就是String字符串:
String s = "hello";引用类型的变量类似于C语言的指针,它内部存储一个“地址”,指向某个对象在内存的位置,后续我们介绍类的概念时会详细讨论
常量(有点像宏定义)
定义变量的时候,如果加上final修饰符,这个变量就变成了常量:
final double PI = 3.14; // PI是一个常量 double r = 5.0; double area = PI * r * r; PI = 300; // compile error!常量在定义时进行初始化后就不可再次赋值,再次赋值会导致编译错误。
常量的作用是用有意义的变量名来避免魔术数字(Magic number),例如,不要在代码中到处写3.14,而是定义一个常量。如果将来需要提高计算精度,我们只需要在常量的定义处修改,例如,改成3.1416,而不必在所有地方替换3.14。
为了和变量区分开来,根据习惯,常量名通常全部大写。
var关键字(有点像c中的结构体)
有些时候,类型的名字太长,写起来比较麻烦。例如:
StringBuilder sb = new StringBuilder();这个时候,如果想省略变量类型,可以使用var关键字:
var sb = new StringBuilder();编译器会根据赋值语句自动推断出变量sb的类型是StringBuilder。对编译器来说,语句:
var sb = new StringBuilder();实际上会自动变成:
StringBuilder sb = new StringBuilder();因此,使用var定义变量,仅仅是少写了变量类型而已。
整数运算
ava的整数运算遵循四则运算规则,可以使用任意嵌套的小括号。四则运算规则和初等数学一致。例如:
// 四则运算 public class Main { public static void main(String[] args) { int i = (100 + 200) * (99 - 88); // 3300 int n = 7 * (5 + (i - 9)); // 23072 System.out.println(i); System.out.println(n); } }整数的数值表示不但是精确的,而且整数运算永远是精确的,即使是除法也是精确的,因为两个整数相除只能得到结果的整数部分:
int x = 12345 / 67; // 184求余运算使用%:
int y = 12345 % 67; // 12345÷67的余数是17溢出(直接用更大的的数据类型即可,long,longlong,没多占多少内存)
要特别注意,整数由于存在范围限制,如果计算结果超出了范围,就会产生溢出,而溢出不会出错,却会得到一个奇怪的结果:
// 运算溢出 public class Main { public static void main(String[] args) { int x = 2147483640; int y = 15; int sum = x + y; System.out.println(sum); // -2147483641 } }要解释上述结果,我们把整数2147483640和15换成二进制做加法:
0111 1111 1111 1111 1111 1111 1111 1000 + 0000 0000 0000 0000 0000 0000 0000 1111 ----------------------------------------- 1000 0000 0000 0000 0000 0000 0000 0111由于最高位计算结果为1,因此,加法结果变成了一个负数。
特别注意:整数的除法对于除数为0时运行时将报错,但编译不会报错。
其余还剩自增自减,位移运算,位运算,都与c语言一致,我就不讲了)
运算优先级(这个在c中算是一个容易混淆的知识点)
在Java的计算表达式中,运算优先级从高到低依次是:
()!~++--*/%+-<<>>>>>&|+=-=*=/=
记不住也没关系,只需要加括号就可以保证运算的优先级正确。
浮点数运算
浮点数运算和整数运算相比,只能进行加减乘除这些数值计算,不能做位运算和移位运算。
在计算机中,浮点数虽然表示的范围大,但是,浮点数有个非常重要的特点,就是浮点数常常无法精确表示。
举个例子:
浮点数0.1在计算机中就无法精确表示,因为十进制的0.1换算成二进制是一个无限循环小数,很显然,无论使用float还是double,都只能存储一个0.1的近似值。但是,0.5这个浮点数又可以精确地表示。
因为浮点数常常无法精确表示,因此,浮点数运算会产生误差:
// 浮点数运算误差 public class Main { public static void main(String[] args) { double x = 1.0 / 10; double y = 1 - 9.0 / 10; // 观察x和y是否相等: System.out.println(x); System.out.println(y); } }由于浮点数存在运算误差,所以比较两个浮点数是否相等常常会出现错误的结果。正确的比较方法是判断两个浮点数之差的绝对值是否小于一个很小的数:
// 比较x和y是否相等,先计算其差的绝对值: double r = Math.abs(x - y); // 再判断绝对值是否足够小: if (r < 0.00001) { // 可以认为相等 } else { // 不相等 }浮点数在内存的表示方法和整数比更加复杂。Java的浮点数完全遵循IEEE-754标准,这也是绝大多数计算机平台都支持的浮点数标准表示方法。
布尔运算
对于布尔类型boolean,永远只有true和false两个值。
布尔运算是一种关系运算,包括以下几类:
- 比较运算符:
>,>=,<,<=,==,!= - 与运算
&& - 或运算
|| - 非运算
!
下面是一些示例:
boolean isGreater = 5 > 3; // true int age = 12; boolean isZero = age == 0; // false boolean isNonZero = !isZero; // true boolean isAdult = age >= 18; // false boolean isTeenager = age >6 && age <18; // true关系运算符的优先级从高到低依次是:
!>,>=,<,<===,!=&&||
短路运算
布尔运算的一个重要特点是短路运算。如果一个布尔运算的表达式能提前确定结果,则后续的计算不再执行,直接返回结果。
因为false && x的结果总是false,无论x是true还是false,因此,与运算在确定第一个值为false后,不再继续计算,而是直接返回false。
我们考察以下代码:
// 短路运算 public class Main { public static void main(String[] args) { boolean b = 5 < 3; boolean result = b && (5 / 0 > 0); // 此处 5 / 0 不会报错 System.out.println(result); } }如果没有短路运算,&&后面的表达式会由于除数为0而报错,但实际上该语句并未报错,原因在于与运算是短路运算符,提前计算出了结果false。
如果变量b的值为true,则表达式变为true && (5 / 0 > 0)。因为无法进行短路运算,该表达式必定会由于除数为0而报错,可以自行测试。
类似的,对于||运算,只要能确定第一个值为true,后续计算也不再进行,而是直接返回true:
boolean result = true || (5 / 0 > 0); // true三元运算符(就是c语言中的三目运算符)
Java还提供一个三元运算符b ? x : y,它根据第一个布尔表达式的结果,分别返回后续两个表达式之一的计算结果。示例:
// 三元运算 public 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之一。
字符和字符串
在Java中,字符和字符串是两个不同的类型。
字符类型
字符类型char是基本数据类型,它是character的缩写。一个char保存一个Unicode字符:
char c1 = 'A'; char c2 = '中';因为java在内存中总是使用Unicode表示字符,所以,一个英文字符和一个中文字符都用一个char类型表示,它们都占用两个字节。要显示一个字符的Unicode编码,只需将char类型直接赋值给int类型即可:
int n1 = 'A'; // 字母“A”的Unicodde编码是65 int n2 = '中'; // 汉字“中”的Unicode编码是20013还可以直接用转义字符\u+Unicode编码来表示一个字符:
// 注意是十六进制: char c3 = '\u0041'; // 'A',因为十六进制0041 = 十进制65 char c4 = '\u4e2d'; // '中',因为十六进制4e2d = 十进制20013字符串类型
和char类型不同,字符串类型String是引用类型,我们用双引号"..."表示字符串。一个字符串可以存储0个到任意个字符:
String s = ""; // 空字符串,包含0个字符 String s1 = "A"; // 包含一个字符 String s2 = "ABC"; // 包含3个字符 String s3 = "中文 ABC"; // 包含6个字符,其中有一个空格因为字符串使用双引号"..."表示开始和结束,那如果字符串本身恰好包含一个"字符怎么表示?例如,"abc"xyz",编译器就无法判断中间的引号究竟是字符串的一部分还是表示字符串结束。这个时候,我们需要借助转义字符\:
String s = "abc\"xyz"; // 包含7个字符: a, b, c, ", x, y, z因为\是转义字符,所以,两个\\表示一个\字符:
String s = "abc\\xyz"; // 包含7个字符: a, b, c, \, x, y, z常见的转义字符包括:
\"表示字符"\'表示字符'\\表示字符\\n表示换行符\r表示回车符\t表示Tab\u####表示一个Unicode编码的字符
例如:
String s = "ABC\n\u4e2d\u6587"; // 包含6个字符: A, B, C, 换行符, 中, 文字符串连接(相当于c语言中的stract,方便了很多,也体现了java面向对象的特点)
Java的编译器对字符串做了特殊照顾,可以使用+连接任意字符串和其他数据类型,这样极大地方便了字符串的处理。例如:
// 字符串连接 public class Main { public static void main(String[] args) { String s1 = "Hello"; String s2 = "world"; String s = s1 + " " + s2 + "!"; System.out.println(s); // Hello world! } }如果用+连接字符串和其他数据类型,会将其他数据类型先自动转型为字符串,再连接:
// 字符串连接 public class Main { public static void main(String[] args) { int age = 25; String s = "age is " + age; System.out.println(s); // age is 25 } }多行字符串
如果我们要表示多行字符串,使用+号连接会非常不方便:
String s = "first line \n" + "second line \n" + "end";从Java 13开始,字符串可以用"""..."""表示多行字符串(Text Blocks)了。举个例子:
// 多行字符串 public class Main { public static void main(String[] args) { String s = """ SELECT * FROM users WHERE id > 100 ORDER BY name DESC """; System.out.println(s); } }上述多行字符串实际上是5行,在最后一个DESC后面还有一个\n。如果我们不想在字符串末尾加一个\n,就需要这么写:
String s = """ SELECT * FROM users WHERE id > 100 ORDER BY name DESC""";还需要注意到,多行字符串前面共同的空格会被去掉,即:
String s = """ ...........SELECT * FROM ........... users ...........WHERE id > 100 ...........ORDER BY name DESC ...........""";用.标注的空格都会被去掉。
如果多行字符串的排版不规则,那么,去掉的空格就会变成这样:
String s = """ ......... SELECT * FROM ......... users .........WHERE id > 100 ......... ORDER BY name DESC ......... """;即总是以最短的行首空格为基准。
不可变特性(有点像指针的感觉)
Java的字符串除了是一个引用类型外,还有个重要特点,就是字符串不可变。考察以下代码:
观察执行结果,难道字符串s变了吗?其实变的不是字符串,而是变量s的“指向”。
执行String s = "hello";时,JVM虚拟机先创建字符串"hello",然后,把字符串变量s指向它:
s │ ▼ ┌───┬───────────┬───┐ │ │ "hello" │ │ └───┴───────────┴───┘紧接着,执行s = "world";时,JVM虚拟机先创建字符串"world",然后,把字符串变量s指向它:
s ──────────────┐ │ ▼ ┌───┬───────────┬───┬───────────┬───┐ │ │ "hello" │ │ "world" │ │ └───┴───────────┴───┴───────────┴───┘原来的字符串"hello"还在,只是我们无法通过变量s访问它而已。因此,字符串的不可变是指字符串内容不可变。至于变量,可以一会指向字符串"hello",一会指向字符串"world"。
理解了引用类型的“指向”后,试解释下面的代码输出:
// 字符串不可变 public class Main { public static void main(String[] args) { String s = "hello"; String t = s; s = "world"; System.out.println(t); // t是"hello"还是"world"? } }空值null
引用类型的变量可以指向一个空值null,它表示不存在,即该变量不指向任何对象。例如:
String s1 = null; // s1是null String s2 = s1; // s2也是null String s3 = ""; // s3指向空字符串,不是null注意要区分空值null和空字符串"",空字符串是一个有效的字符串对象,它不等于null。
数组类型
如果我们有一组类型相同的变量,例如,5位同学的成绩,可以这么写:
public class Main { public static void main(String[] args) { // 5位同学的成绩: int n1 = 68; int n2 = 79; int n3 = 91; int n4 = 85; int n5 = 62; } }但其实没有必要定义5个int变量。可以使用数组来表示“一组”int类型。代码如下:
// 数组 public class Main { public static void main(String[] args) { // 5位同学的成绩: int[] ns = new int[5]; ns[0] = 68; ns[1] = 79; ns[2] = 91; ns[3] = 85; ns[4] = 62; } }定义一个数组类型的变量,使用数组类型“类型[]”,例如,int[]。和单个基本类型变量不同,数组变量初始化必须使用new int[5]表示创建一个可容纳5个int元素的数组。
Java的数组有几个特点:
- 数组所有元素初始化为默认值,整型都是
0,浮点型是0.0,布尔型是false; - 数组一旦创建后,大小就不可改变。
要访问数组中的某一个元素,需要使用索引。数组索引从0开始,例如,5个元素的数组,索引范围是0~4。
可以修改数组中的某一个元素,使用赋值语句,例如,ns[1] = 79;。(很像指针,其实在内存的角度来看就是指针,所以用指针的思想就好理解了)
可以用数组变量.length获取数组大小:(相当于strlen)
// 数组 public class Main { public static void main(String[] args) { // 5位同学的成绩: int[] ns = new int[5]; System.out.println(ns.length); // 5 } }也可以在定义数组时直接指定初始化的元素,这样就不必写出数组大小,而是由编译器自动推算数组大小。例如:
// 数组 public class Main { public static void main(String[] args) { // 5位同学的成绩: int[] ns = new int[] { 68, 79, 91, 85, 62 }; System.out.println(ns.length); // 编译器自动推算数组大小为5 } }还可以进一步简写为:
int[] ns = { 68, 79, 91, 85, 62 };注意数组是引用类型,并且数组大小不可变。我们观察下面的代码:
// 数组 public class Main { public static void main(String[] args) { // 5位同学的成绩: int[] ns; ns = new int[] { 68, 79, 91, 85, 62 }; System.out.println(ns.length); // 5 ns = new int[] { 1, 2, 3 }; System.out.println(ns.length); // 3 } }数组大小变了吗?看上去好像是变了,但其实根本没变。
对于数组ns来说,执行ns = new int[] { 68, 79, 91, 85, 62 };时,它指向一个5个元素的数组:
ns │ ▼ ┌───┬───┬───┬───┬───┬───┬───┐ │ │68 │79 │91 │85 │62 │ │ └───┴───┴───┴───┴───┴───┴───┘执行ns = new int[] { 1, 2, 3 };时,它指向一个新的3个元素的数组:
ns ──────────────────────┐ │ ▼ ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ │ │68 │79 │91 │85 │62 │ │ 1 │ 2 │ 3 │ │ └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘但是,原有的5个元素的数组并没有改变,只是无法通过变量ns引用到它们而已。
字符串数组
如果数组元素不是基本类型,而是一个引用类型,那么,修改数组元素会有哪些不同?
字符串是引用类型,因此我们先定义一个字符串数组:
String[] names = { "ABC", "XYZ", "zoo" };对于String[]类型的数组变量names,它实际上包含3个元素,但每个元素都指向某个字符串对象:
┌─────────────────────────┐ names │ ┌─────────────────────┼───────────┐ │ │ │ │ │ ▼ │ │ ▼ ▼ ┌───┬───┬─┴─┬─┴─┬───┬───────┬───┬───────┬───┬───────┬───┐ │ │░░░│░░░│░░░│ │ "ABC" │ │ "XYZ" │ │ "zoo" │ │ └───┴─┬─┴───┴───┴───┴───────┴───┴───────┴───┴───────┴───┘ │ ▲ └─────────────────┘对names[1]进行赋值,例如names[1] = "cat";,效果如下:
┌─────────────────────────────────────────────────┐ names │ ┌─────────────────────────────────┐ │ │ │ │ │ │ ▼ │ │ ▼ ▼ ┌───┬───┬─┴─┬─┴─┬───┬───────┬───┬───────┬───┬───────┬───┬───────┬───┐ │ │░░░│░░░│░░░│ │ "ABC" │ │ "XYZ" │ │ "zoo" │ │ "cat" │ │ └───┴─┬─┴───┴───┴───┴───────┴───┴───────┴───┴───────┴───┴───────┴───┘ │ ▲ └─────────────────┘这里注意到原来names[1]指向的字符串"XYZ"并没有改变,仅仅是将names[1]的引用从指向"XYZ"改成了指向"cat",其结果是字符串"XYZ"再也无法通过names[1]访问到了。
对“指向”有了更深入的理解后,试解释如下代码:
// 数组 public class Main { public static void main(String[] args) { String[] names = {"ABC", "XYZ", "zoo"}; String s = names[1]; names[1] = "cat"; System.out.println(s); // s是"XYZ"还是"cat"? } }输入输出
输出
在前面的代码中,我们总是使用System.out.println()来向屏幕输出一些内容。
println是print line的缩写,表示输出并换行(=printf("\n"))。因此,如果输出后不想换行,可以用print():
// 输出 public class Main { public static void main(String[] args) { System.out.print("A,"); System.out.print("B,"); System.out.print("C."); System.out.println(); System.out.println("END"); } }注意观察上述代码的执行效果。
格式化输出
Java还提供了格式化输出的功能。为什么要格式化输出?因为计算机表示的数据不一定适合人来阅读:
// 格式化输出 public class Main { public static void main(String[] args) { double d = 12900000; System.out.println(d); // 1.29E7 } }如果要把数据显示成我们期望的格式,就需要使用格式化输出的功能。格式化输出使用System.out.printf(),通过使用占位符%?,printf()可以把后面的参数格式化成指定格式:
// 格式化输出 public class Main { public static void main(String[] args) { double d = 3.1415926; System.out.printf("%.2f\n", d); // 显示两位小数3.14 System.out.printf("%.4f\n", d); // 显示4位小数3.1416 } }Java的格式化功能提供了多种占位符,可以把各种数据类型“格式化”成指定的字符串:
| 占位符 | 说明 |
|---|---|
| %d | 格式化输出整数 |
| %x | 格式化输出十六进制整数 |
| %f | 格式化输出浮点数 |
| %e | 格式化输出科学计数法表示的浮点数 |
| %s | 格式化字符串 |
注意,由于%表示占位符,因此,连续两个%%表示一个%字符本身。
占位符本身还可以有更详细的格式化参数。下面的例子把一个整数格式化成十六进制,并用0补足8位:
// 格式化输出 public class Main { public static void main(String[] args) { int n = 12345000; System.out.printf("n=%d, hex=%08x", n, n); // 注意,两个%占位符必须传入两个数 } }详细的格式化参数请参考JDK文档java.util.Formatter
输入
和输出相比,Java的输入就要复杂得多。
我们先看一个从控制台读取一个字符串和一个整数的例子:
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); // 创建Scanner对象 System.out.print("Input your name: "); // 打印提示 String name = scanner.nextLine(); // 读取一行输入并获取字符串 System.out.print("Input your age: "); // 打印提示 int age = scanner.nextInt(); // 读取一行输入并获取整数 System.out.printf("Hi, %s, you are %d\n", name, age); // 格式化输出 } }首先,我们通过import语句导入java.util.Scanner,import是导入某个类的语句,必须放到Java源代码的开头,后面我们在Java的package中会详细讲解如何使用import。
然后,创建Scanner对象并传入System.in。System.out代表标准输出流,而System.in代表标准输入流。直接使用System.in读取用户输入虽然是可以的,但需要更复杂的代码,而通过Scanner就可以简化后续的代码。
有了Scanner对象后,要读取用户输入的字符串,使用scanner.nextLine(),要读取用户输入的整数,使用scanner.nextInt()。Scanner会自动转换数据类型,因此不必手动转换。
要测试输入,必须从命令行读取用户输入,因此,需要走编译、执行的流程:
$ javac Main.java这个程序编译时如果有警告,可以暂时忽略它,在后面学习IO的时候再详细解释。编译成功后,执行:
$ java Main Input your name: Bob ◀── 输入 Bob Input your age: 12 ◀── 输入 12 Hi, Bob, you are 12 ◀── 输出根据提示分别输入一个字符串和整数后,我们得到了格式化的输出。
三最后一语
今天真是爆更了,一万四千字,之前有这个零头就不错了。主要是想快点进入数据结构与算法真题的节奏,所以这次量大管饱的更新大家要好好吸收。
对每个人而言,真正的职责只有一个:找到自我。然后在心中坚守其一生,全心全意,永不停息。
---赫尔曼.黑塞《德米安:彷徨少年时》
感谢观看,共勉!!