java程序执行过程
java程序运行的过程如下:
Java基本数据类型
Java 是种强类型语言 。 这就意味着必须为每一个变量声明一种类型 : 在 Java 中 ,共有 8种基本类型 ( primitive type ) , 其中有 4 种整型 、 2 种浮点类型 、 1 种用于表示 Unicode 编码的字符单元的字符类型 char
整型
整型用于表示没有小数部分的数值 , 它允许是负数,在 Java 中, 整型的范围与运行 Java 代码的机器无关 。 这就解决了软件从一个平台移植到另一个平台 , 或者在同一个平台中的不同操作系统之间进行移植给程序员带来的诸多问题 。 Java 没有任何无符号 ( unsigned ) 形式的 int 、 long 、 short 或 byte 类型。
浮点型
浮点类型用于表示有小数部分的数值 。float 类型的数值有一个后缀 F 或 f ( 例如 , 3.14F)。没有后缀 F 的浮点数值 ( 如 3.14 ) 默认为 double 类型。
char类型
char 类型的字面量值要用单引号括起来 。 例如 : W 是编码值为 65 所对应的字符常量。它与 " A " 不同, " A " 是包含一个字符 A 的字符串 , char 类型的值可以表示为十六进制值 , 其范围从 \u0000 到 \ Uffff 。
boolean类型
boolean ( 布尔)类型有两个值 : false 和 true , 用来判定逻辑条件不能进行相互转换 。整型值和布尔值之间不能进行相互转换 。
演示程序:
public class Main {
public static void main(String[] args) {
int x = 247483647; // 4byte
short y = 32767; // 2byte
long z = 9223372036854775807L; // 8byte
byte k = 127; // 1byte
float f = 3.40282347E+38F; //4byte
double d = 1.79769313486231570E+308; // 8byte
char c = 'a'; // 2byte
boolean bool = true; // 1byte
}
}
变量
变量初始化
Java中变量名必须是一个以字母开头并由字母或数字构成的序列 。字母包括 ‘A ‘ ~ ‘Z ’ 、 ’a ’ ~’z‘、‘_’,’$’
或在某种语言中表示字母的任何 Unicode 字符 。不能使用 Java 关键字作为变量名。在 Java 中, 不区分变量的声明与定义 。
声明一个变量之后, 必须用赋值语句对变量进行显式初始化, 千万不要使用未初始化的变量 。 例如 , Java 编译器认为下面的语句序列是错误的 :
int vacationDays;
System out println(vacationDays) : // ERROR variable not initialized
在方法中变量最好首先初始化,类中的变量编译器会给他们初始化值,参数传递肯定被初始化了。
常量
在 Java 中, 利用关键字 final 指示常量 。关键字 final 表示这个变量只能被赋值一次 。 一旦被赋值之后, 就不能够再更改了 。 习惯上 ,常量名使用全大写 。 例如 :
final double CM _ PER INCH = 2.54 ;
在 Java 中, 经常希望某个常量可以在一个类中的多个方法中使用 , 通常将这些常量称为类常量 ,在main方法中使用final,只能在main方法中使用相关常量。
public class Main {
public static final double CM_PER_INCH = 2.54 ;
public static void main(String[] args) {
System.out.println(Main.finalDou());
}
public static double finalDou() {
return CM_PER_INCH;
}
}
运算符
在 Java 中, 使用算术运算符 + 、 - 、 * 、 / 表示加 、 减 、 乘、 除运算 。 当参与 / 运算的两个操作数都是整数时 , 表示整数除法 ; 否则, 表示浮点除法 。整数的求余操作用 % 表示 。 例如, 15/ 2 等于7,15 %2 等于 1 , 15.0/2 等于 7.50。需要注意 , 整数被 0 除将会产生一个异常, 而浮点数被 0 除将会得到无穷大或 NaN 结果 。
Math类
Java中的Math类提供了常用的数学运算:
Math sin
Math . cos
Math . tan
Math . atan
Math . atan2
Math . exp
Math . log
Math . log10
Java 还提供了两个用于表示 PI 和 e 常量的近似值
Math.PI
Math E
数值类型之间的转换
在图中有 6 个实心箭头 , 表示无信息丢失的转换 ; 有 3 个虚箭头, 表示可能有精度损失的转换 。例如 , 123 456 789 是一个大整数 , 它所包含的位数比 float 类型所能够表达的位数多。当将这个整型数值转换为 float 类型时 , 将会得到同样大小的结果 , 但却失去了一定的精度 。
强制类型转换
有时也需要将 double 转换成 int 。 在 Java 中, 允许进行这种数值之间的类型转换 。有可能会丢失一些信息 。 在这种情况下 , 需要通过强制类型转换 ( cast ) 实现这个操作 。
double x * 9.997;
int nx = (int)x;
这样 , 变量 nx 的值为 9 。 强制类型转换通过截断小数部分将浮点值转换为整型 。
如果想对浮点数进行舍人运算, 以便得到最接近的整数,那就需要使用 Math round 方法 :
double x = 9.997;
int nx = (int) Math.round(x) ;
现在 , 变量 nx 的值为 10 。
位运算符
& ( 与):x & y
| (或):x | y
^( 异或):x ^ y
~ (非):~x
字符串
Java 没有内置的字符串类型, 而是在标准 Java 类库中提供了一个预定义类 , 很自然地叫做 String 。 每个用双引号括起来的字符串都是 String 类的一个实例 :
String e = ""; / / an empty string
String greeting = "Hello " ;
字串
String 类的 substring 方法可以从一个较大的字符串提取出一个子串 。例如 :
String greeting = " Hello";
String s = greeting.substring (0, 3) ; // 在substring中从0开始计数,直到3为止, 但不包含 。
创建了一个由字符 “ Hel ” 组成的字符串 。
拼接
Java 语言允许使用 + 号连接 (拼接) 两个字符串 。
String expletive = "Expletive" ;
String PC13 = " deleted" ;
String message = expletive + PC13;
当将一个字符串与一个非字符串的值进行拼接时 , 后者被转换成字符串(任何一个 Java 对象都可以转换成字符串 )例如 :
int age= 13;
String rating = " PC " + age ; // rating 设置为 “PG13”
如果需要把多个字符串放在一起 , 用一个定界符分隔 , 可以使用静态 join 方法 :
String all = String join ( " / " , " S " , " M " , " L " , " XL " ) ; // all is the string " S / H / L / XL "
不可变字符串
如果希望将字符串s的内容修改为 “ Help ! ” ,不能直接地将s 的最后两个位置的字符修改为‘ p ’ 和‘ ! ‘这对于 C 程序员来说,将会感到无从下手 。
s = s.substring ( 0 , 3 ) + " p! " ;
或者
String s = "Hello";
s="Help!";
代表着原来的"hello"一直存在于内存,但是无法通过s访问了,s引用变量指向改变,内容变成了“Help!”,这就是不可变字符串。
检测字符串内容是否相等
可以使用 equals 方法检测两个字符串是否相等 。 对于表达式 :
s.equals(t)
如果字符串 s 与字符串 t 内容一样 , 则返回 true ; 否则 , 返回 false 。
一定不要使用 == 运算符检测两个字符串是否相等! 这个运算符只能够确定两个字串是否放置在同一个位置上 。 当然, 如果字符串放置在同一个位置上, 它们必然相等 。 但是,完全有可能将内容相同的多个字符串的拷贝放置在不同的位置上 。
String greeting = "Hello";
System.out.println(greeting == "Hello"); // true
System.out.println(greeting.substring(0,3) == "Hel"); // false
如果虚拟机始终将相同的字符串共享 , 就可以使用 == 运算符检测是否相等 。实际上只有字符串常量是共享的,而 + 或 substring 等操作产生的结果并不是共享的 。
空串与null串
空串 “” 是长度为 0 的字符串 。 可以调用以下代码检查一个字符串是否为空 :
if ( str.length() = 0)
或者
if ( str.equals ("" ))
空串是一个 Java 对象 , 有自己的串长度 ( 0 ) 和内容 (空)。 不过, String 变量还可以存放一个特殊的值 , 名为 null , 这表示目前没有任何对象与该变量关联,什么都不指向。要检查一个字符串是否为 null , 要使用以下条件 :
if ( str == null )
有时要检查一个字符串既不是 null 也不为空串, 这种情况下就需要使用以下条件 :
if (str ! = null && str lengthO != 0 )
首先要检查 str 不为 null。如果在一个 mill 值上调用方法, 会出现错误,因为什么也不指向,没有任何方法供调用。
码点与代码单元
代码点(Code Point):在 Unicode 代码空间中的一个值,取值 0x0 至 0x10FFFF,代表一个字符,是指一个编码表中的某个字符对应的代码值。
代码单元(Code Unit):在具体编码形式中的最小单位。比如 UTF-16 中一个 code unit 为 16 bits,UTF-8 中一个 code unit 为 8 bits。一个 code point 可能由一个或多个 code unit(s) 表示。在 U+10000 之前的 code point 可以由一个 UTF-16 code unit 表示,U+10000 及之后的 code point 要由两个 UTF-16 code units 表示。
String hello = "hi𝕆🤪";
System.out.println(hello.length()); // 代码单元数量6
System.out.println(hello.codePointCount(0, hello.length())); //码点数量4
要想得到第 i 个码点 , 应该使用下列语句
int index = greeting.offsetByCodePoints(0,i); // 得到第i个代码单元的位置
int cp = greeting.codePointAt(index); // 得到码点
String API
Java 中的 String 类包含了 50 多个方法 。绝大多数都很有用, 可以设想使用的频繁非常高 。
在 API 注释中, 有一些 CharSequence 类型的参数这是一种接口类型。 只要看到串都属于这个接口,完全可以传入 String 类型的实参 。
构建字符串
有些时候, 需要由较短的字符串构建字符串 , 例如 , 按键或来自文件中的单词 。 采用字符串连接的方式达到此目的效率比较低 。 每次连接字符串 , 都会构建一个新的 String 对象,既耗时 , 又浪费空间 。 使用 StringBuilder类就可以避免这个问题的发生 。
如果需要用许多小段的字符串构建一个字符串, 那么应该按照下列步骤进行 。 首先 , 构
建一个空的字符串构建器 :
StringBuilder builder = new StringBuilder(); // inital value is 16
当每次需要添加一部分内容时 , 就调用 append 方法 。
builder.append (ch) ; // appends a single character
builder.append (str) ; // appends a string
builder.insert(int offset , String str); // insert a string in offset
如果所有字符串在一个单线程中编辑,则应该用 StringBuilder 替代它,多线程使用StringBuffer 。
输入输出
读取输入
要想通过控制台进行输人, 首先需要构造一个 Scanner 对象, 并与 “ 标准输人流 ” System.in 关联 。
Scanner in = new Scanner (System . in ) ;
例如 , nextLine 方法将输入一行 。
System out print ( " What is your name ? " ) ;
String name = in nextLine () ;
要想读取一个单词 ( 以空白) 就调用。
String firstName = in . next() ;
要想读取一个整数 , 就调用 nextlnt 方法 。
System out print ( " How old are you ? " ) ;
int age = in nextlnt() ;
格式化输出
Java SE 5.0 沿用了 C语言库函数中的 printf 方法 。
double x = 10000.0/3.0;
System.out.printf("%8.2f",x);
可以用 8 个字符的宽度和小数点后两个字符的精度打印 x 。
文件读取和写入
要想对文件进行读取 , 就需要一个用 File 对象构造一个 Scanner 对象 , 如下所示 :
Scanner in = new Scanner(Paths.get("/home/hadoop/1.c") , "UTF-8");
要想写入文件, 就需要构造一个 PrintWriter 对象 。 在构造器中 , 只需要提供文件名 :
PrintWriter out = new PrintWriter("/home/hadoop/myfile.txt" , "UTF-8");
out.print(111);
out.close();
可以像输出到 System.out—样使用 print 、 println 以及 printf命令 。
指定一个相对文件名时 , 例如 , “myfile . txt ” , “ mydirectory / myfile . txt ” 或 “ . . / myfile .txt ” , 文件位于 Java 虚拟机启动路径的相对位置 , 如果在命令行方式下用下列命令启动程序 :
java MyProg
启动路径就是命令解释器的当前路後。 然而 , 如果使用集成开发环境, 那么启动路径将由 IDE 控制 。 可以使用下面的调用方式找到路径的位置 :
系统属性
通过java代码获取相关的系统属性值,如下代码:
Properties props = System.getProperties();
props.list(System.out);
其中的可以通过System通过相关字段来获取值:
String dir = System.getProperty("user.dir");
System.out.println(dir);
控制流程
与任何程序设计语言一样 , Java 使用条件语句和循环结构确定控制流程 。当需要对某个表达式的
多个值进行检测时, 可以使用 switch 语句 。Java 的控制流程结构与 C 和 C ++ 的控制流程结构一样 只有很少的例外情况。
块作用域
块 (即复合语句)是指由一对大括号括起来的若干条简单的 Java 语句 。 块确定了变量的作用域 。 一个块可以嵌套在另一个块中 。
public static void main(String[] args)throws Exception {
int n;
{
int k;
}
}
但是 , 不能在嵌套的两个块中声明同名的变量 。 例如, 下面的代码就有错误, 而无法通过编译 :
public static void main(String[] args)throws Exception {
int n;
{
int k;
int n; // Error can ' t redefine n in inner block
}
}
条件语句
在 Java 中, 条件语句的格式为
if ( condition) statement
这里的条件必须用括号括起来 。
例如:
float yourSales = 15.0f;
float target = 11.0f;
String performance = null;
float bonus = 0.0f;
if (yourSales >= target)
{
performance = "Satisfactory " ;
bonus = 100 + 0.01 * ( yourSales - target ) ;
}
else
{
performance = "Unsatisfactory " ;
bonus = 0;
}
else 子句与最邻近的 if 构成一组 。else if和c++一样
循环
和c++类似,包含while,for等等
switch语句
在处理多个选项时 , 使用 if / else 结构显得有些笨拙 。 Java 有一个与 C / C ++ 完全一样的switch 语句 。
如果建立一个如图 3 - 13 所示的包含 4 个选项的菜单系统, 可以使用下列代码 :
Scanner in = new Scanner (System in ) ;
System out printC ' Select an option ( 1 , 2 , 3 , 4 ) ) ;
int choice = in nextlnt () ;
switch ( choice )
{
case 1:
. ..
break;
case 2:
. ..
break;
case 3:
. ..
break;
case 4 :
. ..
break;
default :
// bad input
break;
}
switch 语句将从与选项值相匹配的 case 标签处开始执行直到遇到 break 语句 , 或者执行到switch语句的结束处为止 。 如果没有相匹配的 case 标签, 而有 default 子句 , 就执行这个子句 。
case 标签可以是 :
• 类型为 char 、 byte 、 short 或 int 的常量表达式 。
• 枚举常量
• JAVA SE7 开始 case 标签还可以是字符串字面量
中断控制流程语句
与用于退出 switch 语句的 break 语句一样, 它也可以用于退出循环语句 。 例如 ,
与 C++ 不同 , Java 还提供了一种带标签的 break 语句, 用于跳出多重嵌套的循环语句。标签必须放在希望跳出的最外层循环之前 , 并且必须紧跟一个冒号 。
通过执行带标签的 break 跳转到带标签的语句块末尾 。
public static void main(String[] args)throws Exception {
int yourSales = 0;
read_data1:
for(int j = 0 ; j < 9 ; j ++) {
System.out.println("1:" + j);
for (int j2 = 0; j2 < 7; j2++) {
System.out.println("2:" +j2);
break read_data1;
}
}
}
continue语句将控制转移到最内层循环的首部 。continue跳过剩余部分程序,循环继续执行,如果将 continue 语句用于 for 循环中, 就可以跳到 for 循环的 “ 更新 ” 部分 。 例如, 下面这个循环 :
for (count = 1 ; count < = 100 ; count + +)
{
System . out . print ( " Enter a number , - 1 to quit : " ) ;
n = in . nextlntO ;
if (n < 0) continue;
sum += n ; / / not executed if n < 0
}
如果 n<0 , 则 continue 语句跳到 count ++ 语句 。还有一种带标签的 continue 语句, 将跳到与标签匹配的循环首部 。
read_data1:
for(int j = 0 ; j < 9 ; j ++) {
System.out.println("1:" + j);
for (int j2 = 0; j2 < 7; j2++) {
System.out.println("2:" +j2);
continue read_data1;
}
}
大数值
如果基本的整数和浮点数精度不能够满足需求, 那么可以使用 java.math 包中的两个很有用的类:Biglnteger 和 BigDecimal,能使用人们熟悉的算术运算符 (如 : + 和 * ) 处理大数值 。 而需要使用大数值类中的 add 和 multiply 方法 。
BigInteger a = new BigInteger("1234567891234567894566");
BigInteger b = a.add(new BigInteger("123123132223"));
System.out.println(b);
数组
java中的数组类型也是作为一种引用类型存在的,数组是一种数据结构 , 用来存储同一类型值的集合 。 通过一个整型下标可以访问数组中的每一个值 。 例如 , 如果 a 是一个整型数组, a [ i ] 就是数组中下标为 i 的整数 。
应该使用 new 运算符创建数组 。
int[] a= new int[100];
可以使用下面两种形式声明数组
int[] a ;
或
int a[] ;
大多数 使用第一种风格, 因为它将类型 int[] ( 整型数组) 与变量名分开了 。
创建一个数字数组时, 所有元素都初始化为 0 。 boolean 数组的元素会初始化为 false,对象数组的元素则初始化为一个特殊值 null , 这表示这些元素 ( 还 ) 未存放任何对象 。
要想获得数组中的元素个数, 可以使用 array.length 。 例如,
for (int i = 0 ; i < a . length ; i + + )
System out . println (a[i ]) ;
一旦创建了数组, 就不能再改变它的大小 (尽管可以改变每一个数组元素) 如果经常需数组列表有关要在运行过程中扩展数组的大小, 就应该使用另一种数据结构ArrayList
因为数组也是引用类型,具有数组大小不可变的特性.
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指向的数组又是新创建的,和原来的不是同一个区域。
for each循环
Java 有一种功能很强的循环结构 , 可以用来依次处理数组中的每个元素 ( 其他类型的元素集合亦可) 而不必为指定下标值而分心 。
这种增强的 for 循环的语句格式为 :
for (variable : collection ) statement
例如 ,
String[] s = new String[10];
for(String ss : s) {
System.out.println(ss);
}
打印数组 s的每一个元素 , 一个元素占一行 。
有个更加简单的方式打印数组中的所有值 , 即利用 Arrays 类的 toString 方法 。 调用 Arrays . toString ( a ) , 返回一个包含数组元素的字符串, 这些元素被放置在括号内, 并用逗号分隔 , 例如 , “ [ 2,3 , 5 , 7 , 11 , 13 ] ” 、 要想打印数组, 可以调用
String[] s = new String[10];
System.out.println(Arrays.toString(s));
数组初始化以及匿名数组
在 Java 中, 提供了一种创建数组对象并同时赋予初始值的简化书写形式 。 下面是一 例子 :
int [] small Primes = { 2 , 3 , 5 , 7 , 11 , 13 } ;
在使用这种语句时 , 不需要调用 new 。甚至还可以初始化一个匿名的数组 :
new int[]{ 17 , 19 , 23 , 29 , 31 , 37 }
在 Java 中 , 允许数组长度为 0 。 在编写一个结果为数组的方法时 , 如果碰巧结果为空 , 则这种语法形式就显得非常有用 。 此时可以创建一个长度为 0 的数组 :
new elementType[0]
注意 , 数组长度为 0 与 null 不同。
数组拷贝
在 Java 中 , 允许将一个数组变量拷贝给另一个数组变量 。 这时, 两个变量将引用同一 个数组:
public static void main(String[] args)throws Exception {
int[] smallPrimes = new int[] {2,3,5,7,11,14};
int[] luckyNumbers = smallPrimes;
luckyNumbers[5] = 12;
System.out.println(Arrays.toString(smallPrimes));
}
图 3 14 显示了拷贝的结果 。
如果希望将一个数组的所有值拷贝到一个新的数组中去,就要使用 Arrays 类的 copyOf方法 :
int[] copiedLuckyNumbers = Arrays.copyOf(luckyNumbers,luckyNumbers.length * 2) ; // 第二参数为新数组大小
System.out.println(Arrays.toString(copiedLuckyNumbers));
命令行参数
前面已经看到多个使用 Java 数组的示例 。 每一个 Java 应用程序都有一个带 String arg[]参数的 main 方法 。 这个参数表明 main 方法将接收一个字符串数组, 也就是命令行参数。
例如, 下面这个程序 :
public static void main ( String[] args )
{
if ( args.length == 0 || args [0].equals ("h"))
System.out.println("Hello,");
else if ( args[0].equals( "-g" ))
System.out.print( "Goodbye,");
for (int i = 1 ; i < args.length ; i++ )
System.out.print (" " + args[i]);
System.out.println ( "!" );
}
如果使用下面这种形式运行这个程序 :
java Message - g cruel world
args 数组将包含下列内容 :
args [0] : "-g"
args [1] : "cruel"
args [2] : "world"
在 Java 应用程序的 main 方法中 , 程序名并没有存储在 args 数组中。
要想对数值型数组进行排序, 可以使用 Arrays 类中的 sort 方法 :
int[] smallPrimes = new int[] {7,11,14,2,3,5,};
Arrays.sort(smallPrimes);
System.out.println(Arrays.toString(smallPrimes));
这个方法使用了优化的快速排序算法 。
多维数组
多维数组将使用多个下标访问数组元素, 它适用于表示表格或更加复杂的排列形式 。
在 Java 中 , 声明一个二维数组相当简单 。 例如 :
double [][] balances ;
与一维数组一样 , 在调用 new 对多维数组进行初始化之前不能使用它 。 在这里可以这样初始化 :
balances=new double [4] [4] :
另外 , 如果知道数组元素 , 就可以不调用 new , 而直接使用简化的书写形式对多维数组进行初始化 。 例如 :
int [] [] magicSquare =
{
{16 , 3 , 2 , 13 } ,
{ 5 , 10 , 11 , 8 } ,
( 9 , 6 , 7 , 12} ,
{ 4 , 15 , 14 , 1}
} ;
一旦数组被初始化, 就可以利用两个方括号访问每个元素, 例如 , balances[i][j]。
要想快速地打印一个二维数组的数据元素列表, 可以调用:
public static void main(String[] args)throws Exception {
int[][] smallPrimes = new int[][] {
{7,11,14,2,3,5},
{7,11,14,2,3,5}
};
Arrays.sort(smallPrimes[0]);
System.out.println(Arrays.deepToString(smallPrimes));
}
输出格式为 : [[2, 3, 5, 7, 11, 14], [7, 11, 14, 2, 3, 5]]
for each循环语句不能自动处理二维数组的每一个元素 。 它是按照行, 也就是一维教组处理的要想访问二维教组 a 的所有元素 , 需要使用两个嵌套的循环 , 如下所示 :
for ( double[] row : a )
for ( double value : row )
do something with value
不规则数组
Java 实际上没有多维数组 , 只有一维数组 。 多维数组被解释为 “ 数组的数组。 ”
balances 数组实际上是一个包含 10 个元素的数组 , 而每个元素又是一个由 6 个浮点数组成的数组。
double[][] balance =new double[10][6];
图解如下:
表达式 balance[i] 引用第 i 个子数组 , 也就是二维表的第 i 行 。它本身也是一个数组 ,balances[i][j]引用这个数组的第 j 项 。
由于可以单独地存取数组的某一行, 所以可以让两行交换 。
double[] temp = balances [i];
balances [i] = balances [i + 1];
balances [i + 1] = temp;
还可以方便地构造一个 “ 不规则 ” 数组 , 即数组的每一行有不同的长度 。二维数组的每个一维数组都是一个一维数组引用变量
案例:
public class Main {
public static final double CM_PER_INCH = 2.54 ;
public static void main(String[] args)throws Exception {
int[][] odds = new int[11][];
for(int n = 0 ; n <=10 ; n ++) {
odds[n] = new int[n + 1];
}
for (int i = 0; i < odds.length; i++) {
for (int j = 0; j < odds[i].length; j++) {
int lotteryOdds = 1;
for(int k = 1 ; k <= j ; k++) {
lotteryOdds = lotteryOdds * (i - k + 1) / k ;
}
odds[i][j] = lotteryOdds;
} // for_j
} // for_i
System.out.println(Arrays.deepToString(odds));
}
}