目录
java结构解析
内置关键字
java内部保留词汇。这些名称不能用作类、方法或变量名称。 |
![]() |
注释
java使用双引号“//”来进行一行的注释,利用“/*”开始注释块和“*/”表示结束。以“/**”开头和“*/”结束的块注释具有特殊用途,允许名为javadoc的程序读取这些注释并自动生成软件文档。
//This is an inline comment.
/*
* This is a block comment.
*/
java 基本数据类型
boolean | 布尔值:true or false |
char | 16位Unicode字符 |
byte | 8位有符号二进制整数 |
short | 16位有符号二进制整数 |
int | 32位有符号二进制整数 |
long | 64位有符号二进制整数 |
float | 32位浮点数 |
double | 64位浮点数 |
java 类和对象
构造器
构造函数是一种特殊的方法,用于初始化新创建的类实例,使其处于一致和稳定的初始状态。构造函数的定义方式与类的其他方法非常相似,但有一些重要区别:
- 构造函数不能是static、abstract或final,因此只允许那些影响可见性的修饰符(即public、protected、private或默认包级别的可见性)。
- 构造函数的名称必须与它构造的类的名称相同
- 我们没有为构造函数指定返回类型(甚至不是void)。构造函数的主体也不会显式返回任何内容。
modifiers name(type0 parameter0 , . . . , typen−1 parametern−1) {
// constructor body . . .
}
构造器初始化顺序
在类的内部,变量定义的先后顺序决定了初始化顺序。即使变量定义散布于方法定义之间,他们仍旧会在任何方法(包括构造器)被调用之前得到初始化。
class Window {
Window(int marker) { print("Window(" + marker + ")"); }
}
class House {
Window w1 = new Window(1); // Before constructor 第一
House() { // Show that we're in the constructor: 第四
print("House()");
w3 = new Window(33); // w3在构造器内再次被初始化
}
Window w2 = new Window(2); // After constructor 第二
void f() { print("f()"); }
Window w3 = new Window(3); // At end 第三
}
public class OrderOfInitialization {
public static void main(String[] args) {
House h = new House();
h.f(); // Shows that construction is done
}
} /* Output:
Window(1)
Window(2)
Window(3)
House()
Window(33)
f()
*/
无论创建多少对象,静态数据都只占有一份存储区域。初始化顺序是先静态对象,而后非静态对象。 Java允许将多个静态初始化动作组织成一个“静态子句”(“静态块”)。与其他静态初始化动作一样,静态块代码仅执行一次:当首次生成这个类的一个对象时,或者首次访问属于那个类的静态成员时(即便从未生成过那个类的对象)。下面为对象创建过程:
- 查询.class文件的路径
- 载入.class文件,执行对象的静态方法或者静态属性域
- new对象,然后分配内存空间(内存空间清零,则对象回收,引用变成null)
- 执行属性域初始化
- 执行构造器
super 关键词
如果没有默认的基类构造器,或想用一个带参数的基类构造器,就必须使用super,显式地调用编写基类构造器的语句,并配以适当的参数列表:
class Game {
Game(int i) {
print("Game constructor");
}
}
class BoardGame extends Game {
BoardGame(int i) {
super(i);// 如果没有这一句,报错,找不到父类相关的构造器
print("BoardGame constructor");
}
}
public class Chess extends BoardGame {
Chess() {
super(11);// 如果没有这一句,报错,找不到父类相关的构造器
print("Chess constructor");
}
public static void main(String[] args) {
Chess x = new Chess();
}
} /* Output:
Game constructor
BoardGame constructor
Chess constructor
*///:~
new关键字
对象是能够承载数据类型值的载体。有三大重要特性:状态、标识和行为。对象的状态就是数据类型中的值。对象标识能够将一个对象区别于另一个对象,也是它在内存中的位置。对象行为就是数据类型的操作。每次调用new()系统都会:
- 为新的对象分配内存空间;所有实例变量都初始化为标准默认值。引用变量的默认值为空,基类型的默认值为0(布尔变量默认值为false
- 使用指定的参数调用新对象的构造函数(与类共享相同名称)初始化对象中的值;
- 返回该对象的一个引用(内存地址), 地址存储在对象变量中,因此,对象变量引用这个新创建的对象;
this 关键词
在Java中的(非静态)方法的主体中,关键字this被自动定义为对调用该方法的实例的引用,指当前对象本身或当前类的一个实例,通过 this 可以调用本对象的所有方法和属性。
- 将引用存储在变量中,或将其作为参数发送给另一个方法,该方法希望该类型的实例作为参数。
public class Demo{
public int x = 10;
public int y = 15;
public void sum(){
// 通过 this 点取成员变量,obj 是 Demo 类的一个实例,this 与 obj 等价
int z = this.x + this.y;
System.out.println("x + y = " + z);
}
public static void main(String[] args) {
Demo obj = new Demo();
obj.sum();
}
}
- 区分实例变量和同名的局部变量
public class Demo{
public String name;
public int age;
public Demo(String name, int age){ //形参的作用域是整个方法体,是局部变量。
this.name = name;
this.age = age;
}
}
- 允许一个构造函数体调用另一个构造函数体。
public Counter( ) {
this(0); // 调用一个值为零的参数构造函数
}
instanceof 运算符
该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。如果运算符左侧变量所指的对象,是操作符右侧类或接口(class/interface)的一个对象,那么结果为真。
( Object reference variable ) instanceof (class/interface type)
修饰词
包访问权限(默认)
默认访问权限没有任何关键词,意味着当前的包中的所有其他类对那个成员都有访问权,但对于包之外的所有类,这个成员是private。
Protected:继承访问权限
被 protected 修饰的成员对于本包和其子类可见,Protect也提供包访问权限,也就是说,相同包内的其他类可以访问protected元素,protected的可见性在于两点:
- 基类的 protected 成员是包内可见的,并且对子类可见;
- 若子类与基类不在同一包中,那么在子类中,子类实例可以访问其从基类继承而来的protected方法,而不能访问基类实例的protected方法。
static 修饰词
当声明一个事物是static,这就意味着这个域或方法不会跟这个类的任何实例关联在一起。方便在没有创建对象的情况下来进行调用(方法/变量), 即被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。
class Statictest{
static int i=47;
}
// 即使创建两个对象,也只有一个i存储空间,两个对象共享
Statictest st1=new Statictest();
Statictest st2=new Statictest();
abstract 修饰词
类和方法可以声明为抽象的,他们只有自己的名字,但没有具体实现。抽象是为了把相同的但不确定的东西的提取出来,为了以后的重用。
final 修饰词
如果类的成员变量声明为final,则通常也将其声明为static,只占据一段不能改变的存储空间,并且使用大写字母命名。一旦Final用于对象引用并被初始化指向一个对象,就无法把他指向另一个对象。对象自身是可以修改的:
private final Value v2 = new Value(22);//Value方法中有i变量
fd1.v2.i++; // Object isn't constant!
//! fd1.v2 = new Value(0); // Error: Can't
字段
声明类的一个或多个实例变量的一般语法如下(可选部分加括号):
[modifiers] type identifier1[=initialValue1], identifier2[=initialValue2];
- 始终尝试将所有字段声明为 private
- 创建一个将这些 private 字段当做输入的构造函数
- 创建一个设置每个 private 字段的 public 方法,这样你就知道何时更改了字段。这些方法叫做 setter
- 创建一个返回每个 private 字段的 public 方法,这样你就能够读取该字段,而不会不小心更改了它。这些方法叫做 getter
方法
如果方法没有返回值,returnType必须为void。Java方法只能返回一个值。为了在Java中返回多个值,我们应该把我们想要返回的所有值组合在一个复合对象中,它的实例变量包括所有要返回的值,然后返回对该复合对象的引用。
[modifiers] returnType methodName(type1 param1 , . . . , typen paramn) {
// method body . . .
}
private 方法通常称之为 helper 方法,因为它们只能被相同的类看到和调用,它们通常是为了帮助整理代码并使代码看起来更简单易读。public 方法是类可以执行的实际操作,基本上是程序的剩余部分能够看到和调用的方法。 以下是何时最好使用 public 方法与 private 方法的示例
class Person{
private String userName;
private String SSN;
private String getId(){
return SSN + "-" + userName;
}
public String getUserName(){
return userName;
}
public boolean isSamePerson(Person p){
if(p.getId().equals(this.getId()){
return true;
}
else{
return false;
}
}
}
字符串
Java的char基类型存储一个表示单个文本字符的值。在Java中,所有可能的字符集是Unicode国际字符集,它是覆盖大多数常用书写语言的16位字符编码。
String类
String 的值是不可变的,每次对String的操作都会生成新的String对象,以包含修改后的字符串内容
索引 Indexing
字符串s中的每个字符c都可以通过使用索引引用,该索引等于s中c之前的字符数。根据此约定,第一个字符位于索引0,最后一个字符位于索引n-1,其中n是字符串的长度。
length() | 返回字符串长度 |
charAt(k) | 返回第k个索引的字符 |
在Java中,“+”操作在对两个字符串作用时执行串接
String term = "over" + "load";
StringBuilder类
Java的字符串类的一个重要特性不可变性(immutable),可以在Java虚拟机内实现高效和优化。但是字符串类型的变量可以重新分配给另一个字符串实例。
String greeting = "Hello";
greeting = "Ciao"; // we changed our mind
greeting = greeting + '!'; // now it is ”Ciao!”
每次使用 System.String类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对象分配新的空间。在需要对字符串执行重复修改的情况下,与创建新的 String对象相关的系统开销可能会非常昂贵。为了支持更有效的字符串编辑,Java提供StringBuilder类,它实际上是字符串的可变版本。
setCharAt(k,c): | 将索引k的字符改为c |
insert(k, s): | 从序列的索引k开始插入字符串s的副本,将现有字符移动以腾出空间。 |
append(s) | 将字符串s附加到序列的末尾。 |
reverse() | 反转当前序列顺序。 |
toString(): | 返回基于当前字符序列的传统字符串实例。 |
数组
存储在数组中的每个值都称为该数组的元素。我们将数组的长度称为它的容量。在Java中,可以使用a.length得到a的数组的长度。数组A的单元格编号为0、1、2直至a.length−1,并且索引为k的单元格通过a[k]访问。
声明和构造数组
- 创建数组的第一种方法是在最初声明数组时使用对文本形式的赋值,使用如下语法:
elementType[ ] arrayName = {initialValue0, initialValue1, . . . , initialValueN−1};
int[ ] primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
- 创建数组的第二种方法是使用new运算符。但是,由于数组不是类的实例,因此我们不使用典型的构造函数语法: 使用new运算符创建数组时,将自动为其所有元素指定元素类型的默认值。
new elementType[length]
double[ ] measurements = new double[1000];
数组复制
将变量备份分配给数据不会创建任何新的数组;它只是为同一个数组创建一个新的别名
int[ ] data = {2, 3, 5, 7, 11, 13, 17, 19};
int[ ] backup;
backup = data; // warning; not a copy
clone()方法生成数组的浅副本,生成一个新数组,该数组的单元格引用第一个数组引用的相同对象。
backup = data.clone();
可以通过重复克隆单个元素来创建深度副本,如下所示,但前提是Person类声明为Cloneable.
Person[ ] guests = new Person[contacts.length];
for (int k=0; k < contacts.length; k++)
guests[k] = (Person) contacts[k].clone( ); // returns Object type
数组结构分析
我们研究的第一个应用是向数组中存储游戏的高分条目,条目是一个键值对包括名称和分数。
public class GameEntry {
private String name;
private int score;
/** Constructs a game entry with given parameters */
public GameEntry(String n,int s){
name = n;
score = s;
}
/**Returns the name field*/
public String getName(){return name;}
/** Returns the score field. */
public int getScore() {return score;}
/** Returns a string representation of this entry. */
@Override
public String toString() {
return "(" + name +","+score+")";
}
}
为了记录得到的分数,设计一个名为记分板的类。一个记分板只能保存一定数量的高分;一旦存储数量达到这一限制,一个新的分数只有在严格高于最低的“高分”时才符合记分板的资格。由于个数限制可能会有所不同,我们允许将它指定为记分板构造函数的参数。
/** Class for storing high scores in an array in nondecreasing order. */
public class Scoreboard {
private int numEntries = 0;
private GameEntry[] board;
public Scoreboard(int capacity){
board = new GameEntry[capacity];
}
// more methods will go here
}
添加条目
public void add(GameEntry e){
int newScore = e.getScore();
if (numEntries < board.length || newScore > board[numEntries-1].getScore()){
if (numEntries < board.length)
numEntries++;
// 向右移动任何较低的分数,为新条目腾出空间
int j = numEntries-1;
while (j>0 && board[j-1].getScore()<newScore){
board[j] = board[j-1];
j--;
}
board[j] = e;
}
}
删除条目
/** Remove and return the high score at index i. */
public GameEntry remove(int i) throws IndexOutOfBoundsException{
if (i<0 || i>=numEntries)
throw new IndexOutOfBoundsException("Invalid index: " + i);
GameEntry temp = board[i];
for (int j=i;j<numEntries-1;j++)
board[j]= board[j+1];
board[numEntries-1] = null;
numEntries--;
return temp;
}
插入排序算法
Algorithm InsertionSort(A):
Input: An array A of n comparable elements
Output: The array A with elements rearranged in nondecreasing order
for k from 1 to n−1 do
Insert A[k] at its proper location within A[0], A[1], . . ., A[k].
public static void insertionSort(char[] data){
int n = data.length;
for (int k=1;k<n;k++){
char cur = data[k];
int j=k;
while (j>0 && data[j-1]>cur){
data[j] = data[j-1];
j--;
}
data[j] = cur;
}
}
java.util.Arrays
由于数组非常重要,Java提供了一个类java.util.Arrays,其中有许多内置的静态方法来执行数组上的常见任务。
equals(A, B) | 仅当数组A和数组B相等时返回true。如果两个数组的元素数目相同,并且两个数组中每个对应的元素对相等,则认为两个数组相等 |
fill(A, x) | 在数组A的每个单元格中存储值x,前提是定义了数组a的类型 |
copyOf(A, n) | 返回一个大小为n的数组,该数组的前k个元素从A复制,其中k=min{n,A.length}。如果n>A.length,则该数组剩余元素将被数据类型的默认值填充 |
copyOfRange(A, s, t) | 返回一个大小为t−s的数组,其中元素为从A[s]复制到A[t−1],其中s<t |
toString(A) | 返回数组A的字符串表示形式,以[开头,以]结尾,显示的元素以字符串“,”分隔。元素a[i]的字符串表示形式使用String.valueOf(A[i])获得,或者调用A[i].toString( ) |
sort(A) | 根据数组元素的自然顺序对数组A排序,该顺序必须是可比较的。 |
binarySearch(A, x) | 在排序数组A中搜索值x,返回找到它的索引,或者在保持排序顺序的同时,返回可以插入它的索引。 |
数组等效性
a == b | 测试A和B是否是相同数组对象 | int[] a = {1,2,3}; |
a.equals(b) | 与 a==b 相同 | a.equals(b) //false a.equals(c) //true |
Arrays.equals(a,b) | 如果数组的长度相同,并所对应位置元素相等,则返回true | Arrays.equals(a,b); //true |
Arrays.deepEquals(a,b) | 支持多维数组的比较,Arrays.deepEquals(a[k],b[k]) |
|
java.util.Random
伪随机数:看起来是随机的数字(但不一定是真正随机的,可以预测给定过去数字列表的序列中的下一个数字)。Java有一个内置类java.util.Random,它的实例是伪随机数生成器。实际上,一个伪随机数生成器是根据当前的数字生成下一个数字,其中a、b和n是适当选择的整数,而%是模运算符:
由于伪随机生成器中的下一个数字是由前一个数字确定的,因此此类生成器始终需要一个启动位置,称为其seed。相同的种子生成的数字序列将始终相同。java.util.random类实例的种子可以在其构造函数中设置,也可以使用其setSeed( )方法设置。每次运行程序时获取不同序列的一个常见技巧,我们可以使用来自用户的定时输入,或者我们可以将种子设置为自1970年1月1日以来的当前时间(由方法System.currentTimeMillis提供)。
nextBoolean( ) | 返回下一个伪随机布尔值 |
nextDouble( ) | 返回下一个伪随机双精度值,介于0.0和1.0之间 |
nextInt( ) | 返回下一个伪随机int值 |
nextInt(n) | 返回从0到n(不包括n)范围内的下一个伪随机int值 |
setSeed(s) | 将此伪随机数生成器的种子设置为 long s |
import java.util.Arrays;
import java.util.Random;
/** Program showing some array uses. */
public class ArrayTest {
public static void main(String[] args){
int data[] = new int[10];
Random rand = new Random();
rand.setSeed(System.currentTimeMillis());
for (int i=0;i<data.length;i++)
data[i] = rand.nextInt(100);
int[] orig= Arrays.copyOf(data, data.length);
System.out.println("arrays equal before sort: "+Arrays.equals(data,orig));
Arrays.sort(data);
System.out.println("arrays equal after sort: " + Arrays.equals(data,orig));
System.out.println("orig = " + Arrays.toString(orig));
System.out.println("data = " + Arrays.toString(data));
}
}
二维数组
Java中的数组是一维的,我们使用单个索引访问数组的每个单元格。我们可以将二维数组定义为一个数组,其中每个单元格都是另一个数组:
int[ ][ ] data = new int[8][10];
Tic-Tac-Toe
基本思想是用二维数组来维护棋盘。Board是一个三乘三矩阵,此数组中的单元格存储的值指示该单元格是否为空或存储X或O。0表示空单元格,1表示x,−1表示o。如果行、列或对角线的值分别加起来为3或−3则胜利。
/** Simulation of a Tic-Tac-Toe game (does not do strategy). */
public class TicTacToe {
public static final int X=1,O=-1;
public static final int EMPTY=0;
private int board[][] = new int[3][3];
private int player;
public TicTacToe(){clearBoard();}
public void clearBoard(){
for (int i=0;i<3;i++)
for (int j=0;j<3;j++)
board[i][j] = EMPTY;
player = X;
}
/** Puts an X or O mark at position i,j. */
public void putMark(int i,int j) throws IllegalArgumentException{
if ((i < 0) || (i > 2) || (j < 0) || (j > 2))
throw new IllegalArgumentException("Invalid board positiin");
if (board[i][j] != EMPTY)
throw new IllegalArgumentException("Board position occupied");
board[i][j] = player;
player = - player;
}
/** Checks whether the board configuration is a win for the given player. */
public boolean isWin(int mark){
return ((board[0][0] + board[0][1] + board[0][2] == mark*3) // row 0
|| (board[1][0] + board[1][1] + board[1][2] == mark*3) // row 1
|| (board[2][0] + board[2][1] + board[2][2] == mark*3) // row 2
|| (board[0][0] + board[1][0] + board[2][0] == mark*3) // column 0
|| (board[0][1] + board[1][1] + board[2][1] == mark*3) // column 1
|| (board[0][2] + board[1][2] + board[2][2] == mark*3) // column 2
|| (board[0][0] + board[1][1] + board[2][2] == mark*3) // diagonal
|| (board[2][0] + board[1][1] + board[0][2] == mark*3)); // rev diag
}
/** Returns the winning player's code, or 0 to indicate a tie (or unfinished game).*/
public int winner(){
if (isWin(X))
return X;
else if (isWin(O))
return O;
else
return 0;
}
/** Returns a simple character string showing the current board. */
public String toString(){
StringBuilder sb = new StringBuilder();
for (int i=0;i<3;i++){
for (int j=0;j<3;j++){
switch (board[i][j]){
case X: sb.append("X");break;
case O: sb.append("O");break;
case EMPTY: sb.append(" ");break;
}
if (j<2) sb.append("|");
}
if (i<2) sb.append("\n-----\n");
}
return sb.toString();
}
/** Test run of a simple game */
public static void main(String[ ] args) {
TicTacToe game = new TicTacToe();
/* X moves: */ /* O moves: */
game.putMark(1,1); game.putMark(0,2);
game.putMark(2,2); game.putMark(0,0);
game.putMark(0,1); game.putMark(2,1);
game.putMark(1,2); game.putMark(1,0);
game.putMark(2,0);
System.out.println(game);
int winningPlayer = game.winner();
String[] outcome = {"O wins", "Tie", "X wins"};
System.out.println(outcome[1 + winningPlayer]);
}
}
包装类型
Java的库中有许多数据结构和算法,它们是专门设计的,以便只与对象类型配合工作。为了绕过这个障碍,Java为每个基类型定义了一个包装类。
基本数据类型 | 包装类名 | 创建示例 | 访问实例 |
---|---|---|---|
boolean | Boolean | obj = new Boolean(true) | obj.booleanValue() |
char | Character | obj = new Charater('Z') | obj.charValue() |
byte | Byte | obj = new Byte((byte) 34) | obj.byteValue() |
short | Short | obj = new Short((short) 100) | obj.shortValue() |
int | Integer | obj = new Integer(1045) | obj.intValue() |
long | Long | obj = new Long(10849L) | obj.longValue() |
float | Float | obj = new Float(3.934F) | obj.floatValue() |
double | Double | obj = new Double(3.934) | obj.doubleValue() |
自动装箱和拆箱
创建int类型值k,Java自动将int封箱,并隐式调用Integer(k)。反过来,对于任意已存在整数值v,Java将自动隐式调用v.intValue( )。其他基本类型的包装纸也进行了类似的转换。并且,所有包装器类型都支持在字符串文本之间来回转换。
int j = 8;
Integer a = new Integer(12);
int k = a; // 隐式调用a.intValue()
int m = j + a; // a在添加前自动解箱
a = 3 * m; // 结果在分配前自动装箱
Integer b = new Integer("-135"); //构造函数接受字符串
int n = Integer.parseInt("2013"); // 使用整数类的静态方法
枚举类型
Java通过定义枚举类型支持从有限集合中选择的更优雅的方法,而不是使用static final每个都声明一下:
modifier enum name { valueName0 , valueName1 , . . . , valueNamen−1 };
public enum Day { MON, TUE, WED, THU, FRI, SAT, SUN };
创建enum时,编译器会为您生成关联的类。 这个类自动从java.lang.Enum继承。一旦定义好day就成为正式数据类型,我们可以用day类型声明变量或参数,并对其进行赋值:
Day today;
today = Day.TUE;
通过调用enum中的values()来遍历enum常量列表。 values()方法按照它们被声明的顺序生成枚举常量的数组,因此可以在foreach循环中使用返回数组。
for(enumtype e : enumtype.values())
表达式
文字
文字是可以在赋值或其他表达式中使用的任何“常量”值。Java允许以下类型的文字:
- null 对象引用
- 布尔值:true和false
- integer:像176或-52这样的整数的默认值是int类型。长整型文字必须以“L”或“l”结尾,例如176l或-52l。
- Floating Point::浮点数(如3.1415和135.23)的默认值是双精度。要指定一个文本是一个Floating,它必须以“F”或“f”结尾。还允许使用指数符号表示的浮点文字,如3.14e2或.19e10;假定基数为10。
- Character:在Java中,字符常量假设取自Unicode字母表。通常,字符定义为单引号中包含的单个符号。例如,“a”和“?'是字符常量。此外,Java定义了以下特殊字符常量:'\n' (newline) '\t' (tab) '\b' (backspace) '\r' (return) '\f' (form feed) '\\' (backslash) '\'' (single quote) '\"' (double quote).
- 字符串文字:字符串文字是用双引号括起来的字符序列
操作符
! | not (prefix) |
&& | conditional and |
|| | conditional or |
~ | 位补码 |
& | 按位与 |
| | 按位或 |
^ | 按位异或 |
<< | 向左移位,用零填充 |
>> | 向右移位,用符号位填充 |
>>> | 向右移位,用零填充 |
类型转换
强制转换是一种允许我们更改值类型的操作。本质上,我们可以取一个类型的值并将其转换为另一个类型的等效值。Java中有两种形式的强制转换:显式和隐式。
显式转换
(type) exp
double d1 = 3.2;
double d2 = 3.9999;
int i1 = (int) d1; // i1 gets value 3
int i2 = (int) d2; // i2 gets value 3
double d3 = (double) i2; // d3 gets value 3.0
String s1 = "2014";
int i1 = Integer.parseInt(s1); // i1 gets value 2014
int i2 = −35;
String s2 = Integer.toString(i2); // s2 gets value ”-35”
隐式转换
有些情况下,Java将根据表达式的上下文执行隐式转换。例在执行扩大转换,而无需显式使用转换运算符。但执行收缩强制转换,则会导致编译器错误。
int i1 = 42;
double d1 = i1; // d1 gets value 42.0
i1 = d1; // 编译错误:可能的精度损失
//要执行到字符串的转换,必须使用适当的ToString方法或通过串联操作执行隐式转换
String s = Integer.toString(22); // this is good
String t = "" + 4.5; // correct, but poor style
String u = "Value = " + 13; // this is good
控制语句
if (firstBooleanExpression)
firstBody;
else if (secondBooleanExpression)
secondBody;
else
thirdBody;
switch (d) {
case MON:
System.out.println("This is tough.");
break;
case TUE:
System.out.println("This is getting better.");
break;
case WED:
System.out.println("Half way there.");
break;
case THU:
System.out.println("I can see the light.");
break;
case FRI:
System.out.println("Now we are talking.");
break;
default:
System.out.println("Day off!");
}
for (initialization; booleanCondition; increment)
loopBody
ForEach Loop
用于数组和容器,表示不必创建int变量去对序列进行计数,foreach将自动产生每一项。任何返回一个数组的方法都可以使用foreach。 String类有一个方法toCharArray(),它返回一个char数组,因此可以很容易的将下面这样迭代在字符串里面的所有字符:
for (elementType name : container)
loopBody
for(char c : "An African Swallow".toCharArray() )
System.out.print(c + " ");
其中container是给定元素类型的数组。值得强调的是,对循环变量进行赋值对底层数组没有影响
public static void scaleBad(double[ ] data, double factor) {
for (double val : data)
val *= factor; // changes local variable only
}
public static void scaleGood(double[ ] data, double factor) {
for (int j=0; j < data.length; j++)
data[j] *= factor; // overwrites cell of the array
}
输出
System.out
Java提供了一个内置的静态对象System.out,,它执行输出到“标准输出”设备。system.out对象是java.io.printstream类的实例。此类定义缓冲输出流的方法,这意味着字符被放在一个称为缓冲区的临时位置,当控制台窗口准备打印字符时,缓冲区将被清空。
print(String s) | 打印字符串s |
print(Object o) | 使用对象本身的ToString方法打印对象o |
print(baseType b) | 打印基类型值b |
println(String s) | 打印字符串s,后跟换行符 |
println(Object o) | 类似于print(Object o),后跟换行符 |
println(baseType b) | 类似于print(Object o),后跟换行符 |
System.out.format()
Format方法模仿C语言的printf(),两者等价:
public class SimpleFormat {
public static void main(String[] args) {
int x = 5;
double y = 5.332542; // The old way:
System.out.println("Row 1: [" + x + " " + y + "]"); // The new way:
System.out.format("Row 1: [%d %f]\n", x, y); // or
System.out.printf("Row 1: [%d %f]\n", x, y);
}
} /* Output:
Row 1: [5 5.332542]
Row 1: [5 5.332542]
Row 1: [5 5.332542]
*/
java.util.Formatter
您可以将Formatter视为翻译器,将格式字符串和数据转换为所需的结果。当您创建一个Formatter对象时,需要向构造器传递一些信息,告诉他最终结果输出到哪(最常用PrintStreams,OutputStream,andFile.)
import java.util.Formatter;
public class Test {
private Formatter formatter = new Formatter(System.out);// 这里需要定义输出的地方
public void print(int a, String b) {
formatter.format("%d%s", a, b);
}
public static void main(String[] args) {
int a = 1;
String b = "b";
new Test().print(a, b);
}
} /* output:
1b*///:~
要在插入数据时控制间距和对齐,您需要更详细的格式修饰符:
%[argument_index$][flags][width][.precision]conversion
width控制一个域的最小尺寸。Formatter对象通过在必要时添加空格,来确保达到某个长度。precision指明最大尺寸:Formatter提供了对空格和对齐的强大控制能力,下面的程序应用格式修饰符来打印一个购物收据:
import java.util.*;
public class Receipt {
private double total = 0;
private Formatter f = new Formatter(System.out);
public void printTitle() {
f.format("%-15s %5s %10s\n", "Item", "Qty", "Price");
f.format("%-15s %5s %10s\n", "----", "---", "-----");
}
public void print(String name, int qty, double price) {
f.format("%-15.15s %5d %10.2f\n", name, qty, price);
total += price;
}
public void printTotal() {
f.format("%-15s %5s %10.2f\n", "Tax", "", total*0.06);
f.format("%-15s %5s %10s\n", "", "", "-----");
f.format("%-15s %5s %10.2f\n", "Total", "",
total * 1.06);
}
public static void main(String[] args) {
Receipt receipt = new Receipt();
receipt.printTitle();
receipt.print("Jack's Magic Beans", 4, 4.25);
receipt.print("Princess Peas", 3, 5.1);
receipt.print("Three Bears Porridge", 1, 14.29);
receipt.printTotal();
}
}
/*
Item Qty Price
---- --- -----
Jack's Magic Be 4 4.25
Princess Peas 3 5.10
Three Bears Por 1 14.29
Tax 1.42
-----
Total 25.06
*/
输入
java.util.Scanner
import java.util.Scanner; // loads Scanner definition for our use
public class InputExample {
public static void main(String[ ] args) {
Scanner input = new Scanner(System.in);
System.out.print("Enter your age in years: ");
double age = input.nextDouble( );
System.out.print("Enter your maximum heart rate: ");
double rate = input.nextDouble( );
double fb = (rate − age) * 0.65;
System.out.println("Your ideal fat-burning heart rate is " + fb);
}
}
scanner类读取输入流并将其划分为tokens,tokens是由分隔符分隔的字符串。
hasNext( ) | 如果输入流中还有其他tokens,则返回true。 |
next( ) | 返回输入流中的下一个tokens字符串;如果没有剩余的tokens,则生成错误。 |
hasNextType( ) | 如果输入流中有另一个tokens,并且可以将其解释为相应的基本类型。则返回true |
nextType( ) | 返回输入流中的下一个tokens所对应于的基本类型;如果没有剩余的tokens或下一个tokens无法解释为基本类型,则生成错误。 |
hasNextLine( ): | 如果输入流有另一行文本,则返回true。 |
nextLine( ): | 将输入推进到当前行尾,并返回跳过的输入。 |
findInLine(String s) | 尝试在当前行中查找与模式s匹配的字符串。如果找到模式,则返回该模式。如果找不到模式,则扫描器返回空值且不前进。 |
Scanner input = new Scanner(System.in);
System.out.print("Please enter an integer: ");
while (!input.hasNextInt( )) {
input.nextLine( );
System.out.print("Invalid integer; please enter an integer: ");
}
int i = input.nextInt( );
软件开发
传统的软件开发涉及几个阶段。三个主要步骤是:1.Design;2.Coding;3.Testing and Debugging
设计
Documentation and Style
Javadoc
为了鼓励使用块注释和自动生成文档,Java编程环境附带了一个文档制作程序JavaDoc。每个javadoc注释都是一个以“/*”开头并以“*/”结尾的块注释,这两个注释之间的每一行都可以以一个星号“*”开头,而忽略该星号。我们使用的主要javadoc标记如下:
- @author text:标识类的作者
- @throws exceptionname description: 描述由此方法发出信号的错误条件
- @param parametername description:描述此方法接受的参数
- @return description:描述方法的返回类型及其值范围
/**
* A simple model for a consumer credit card.
* @author linlinle
* @author Michael T. Goodrich
* @author Roberto Tamassia
*/
public class CreditCard {
private String customer;
private String bank;
private String account;
private int limit;
protected double balance;
/**
* Constructs a new credit card instance.
* @param cust the name of the customer (e.g., ”John Bowman”)
* @param bk the name of the bank (e.g., ”California Savings”)
* @param acnt the account identifier (e.g., ”5391 0375 9387 5309”)
* @param lim the credit limit (measured in dollars)
* @param initialBal the initial balance (measured in dollars)
*/
public CreditCard(String cust, String bk, String acnt, int lim, double initialBal){
customer = cust;
bank = bk;
account = acnt;
limit = lim;
balance = initialBal;
}
/**
*Charges the given price to the card, assuming sufficient credit limit.
* @param price the amount to be charged
* @return true if charge was accepted; false if charge was denied
*/
public boolean charge(double price){
if (price+balance>limit)
return false;
balance +=price;
return true;
}
/**
* Processes customer payment that reduces balance.
* @param amount the amount of payment made
*/
public void makePayment(double amount){
balance -= amount;
}
}