这里写目录标题

  • Eclipse常用快捷键
  • PrimitiveType(基本类型)
  • 3.7.2 **格式化输出**
  • 空格与汉字问题
  • 日期格式化
  • 3.7.3 **文件输入与输出 **
  • 3.9 **大数值**
  • 3.10 **数组**
  • 一维数组
  • 例题1
  • 二维数组
  • 第四章 对象与类
  • 4.1 面向对象程序设计概述
  • 4.2 使用预定义类
  • Employee的实现
  • 4.2.1 java类库中的Date类
  • 4.2.2 Java 类库中的 LocalDate 类
  • 4.3 用户自定义的类
  • 4.3.9 final 实例域
  • 4.4 静态域与静态方法
  • 4 . 4.1 静态域 (类域)
  • 4.4.2 静态常量
  • 4.4.3 静态方法
  • 4.4.4 工厂方法
  • 4.5 方法参数
  • 说明`java`没有`call by reference`
  • 4.6 对 象 构 造
  • 4.6.1 重载
  • 4.6.2 默认域初始化
  • 4.6.3 无参数的构造器
  • 4.6.4 显式域初始化
  • 4.6.5 参数名
  • 4.6.6 调用另一个构造器
  • 4.6.7 初始化块
  • 对象构造实例
  • 4.7 四种访问权限


Eclipse常用快捷键
/*
 * 单行注释:ctrl + / 			取消:ctrl + /
 * 多行注释:ctrl + shift + /  取消:ctrl + shift + \
 * 向下复制一行:ctrl + alt + 下箭头
 * 向上移动代码:alt + 上箭头
 * 删除当行:ctrl + d
 * 格式化:ctrl + shitf + f
 * 智能提示:alt + /
 * main + alt + / + enter
 * syso + alt + / + enter
 */
PrimitiveType(基本类型)

java:

  1. 整数
* byte:	8位:		[-2^7, 2^7-1]
		 * short:	16位:	[-2^15, 2^15-1]
		 * int:		32位:	[-2^31, 2^31-1]大概20亿,10^9级别
		 * long:	64位:	[-2^63, 2^63-1]
  1. 浮点数(IEEE 754标准)注意误差(eps = 10-6取代替等于)
* float:	 32位		sign: 1		exp:8	frac:23
		 * 大概是2^(8-1)-1 = 127 = 2^127 = 10^38这个级别
              
		 * double	64位		sign: 1		exp:11	frac:52
		 * 大概2^(11-1)-1 = 1023 = 2^1023 = 10^307级别
  1. char类型(16位Unicode)
* char     16位(2个字节)
  1. boolean
* boolean	//没有固定大小主要看虚拟机,编译后一般用int代替boolean类型

代码:

public class PrimitiveType {
	public static void main(String[] agrs)
	{
		/*****************************************************/
		byte a1 = 1<<7-1;
		System.out.printf("byte a1 = %d\n", a1);
		short a2 = -(1<<15);
		System.out.printf("short a2 = %d\n", a2);
		int a3 = 'a';
		System.out.printf("int a3 = %d\n", a3);
		long a4 = 1<<63-1L;//long型的结尾需要加L,不加也没报错
		System.out.printf("long a4 = %d\n", a4);
		float f1 = 3.4e38f;//因为float的精度较低(6~7位),所以使用较少,要加f
		System.out.printf("float f1 = %.2f\n", f1);
		double d1 = 1.79e308;
		System.out.printf("double d1 = %f\n", d1);
		/*****************************************************/
		char c1 = 65;
		System.out.printf("char c1 = %c\n", c1);
		char c2 = '中';
		System.out.printf("char c2 = %c\n", c2);
		char c3 = 0x61;//'a'对应的ASCII
		System.out.printf("char c3 = %c\n", c3);
		/*****************************************************/
		boolean b1 = true;
		boolean b2 = false;
		System.out.printf("%b\n", b1 == b2);
	}

}

C++注释

在C++中,一些基本类型可以使用一个或多个类型修饰符进行修饰:
如 signed long int a;//
java没有无符号整数,java的boolean不能赋值0或1

常量

final double PI = 3.14159267;
//C++
const double PI = 3.14;

常用数学函数

public class UseMath {
	public static void main(String[] agrs) {
		//开方
		double res1 = Math.sqrt(4);// 返回值是double
		System.out.println(res1);
		//幂运算
		double res2 = Math.pow(2, 10);
		System.out.println(res2);
		//取余对负数的结果	“欧几里德”规则余数总是要 >= 0
		System.out.printf("%d\t %d\n", -8%12, 8%12);
		//一般解决求余为负的方法
		System.out.printf("%d\n", (-8%2+12)%12);
		//常用常量
		System.out.println(Math.PI);
		System.out.println(Math.E);
		//四舍五入
		System.out.printf("%d\t%d\n", Math.round(5.4), Math.round(5.5));
		int res3 = (int)Math.round(5.1);//函数默认返回long类型,要强制类型转换
	}
}

常用运算符

public class UseOperator {
	public static void main(String[] args) {
		//赋值运算符
		int res1;
		res1 = 1<<10;
		System.out.println(res1);
		res1 += 1024;
		System.out.println(res1);
		res1 >>= 1;
		System.out.println(res1);
		res1 %= 2;
		System.out.println(res1);
		res1 = 1<<10;
		res1 ^= ((1<<10)-1);//少一个括号都不行,所以最后别这样写
		System.out.println(res1);
		//自增&自减,比C++的功能弱一些,不能当左值直接使用
		//位运算和逻辑运算(其余与C++相同)遵循短路原理
		res1 = -2;
		System.out.println(res1>>1);//最高位还是符号位
		System.out.println(res1>>>1);//移动得到的空位以零填充
		System.out.println((1<<31)-1);//移动得到的空位以零填充
	}
}

C++注释

java的>>>运算符很好用,可以直接求2进制中1的个数,而不用转成无符号数

三大循环

public class UseWhile {
	public static void main(String[] args) {
		/*
		 * 1~99的和
		 */
		int sum = 0;
		for(int i = 1, j = 100; i < j; i++) {
			sum += (i+j-j);
		}
		System.out.println(sum);
		/*****************************************/
		int t = 100;
		sum = 0;
		while(t-- != 0)//C++可以t--
		{
			sum += t;
		}
		System.out.println(sum);
		/******************************************/
		int n = 99;
		sum = 0;
		do
		{
			sum += n;
			n--;
		}while(n > 0);
		System.out.println(sum);
	}
}

增强的for语句

//java的for(声明语句 : 表达式)
{
   //代码句子
}
//声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。
//表达式:表达式是要访问的数组名,或者是返回值为数组的方法。
//C++的for(C++11)
for(auto& x : arr)//要修改内容就加&
{
    //代码句子
}

不可变字符串String

C++的string

//C++的string是可变的,所有查找修改功能强大
//可以参考:
//

java的String

/*
		 * String是引用类型,如String str1 = "Hello Word!",
		 * str1是变量可以改变,"Hello Word!"是不可改变的
		 * 修改str1是可以的,但是不能修改str1指向的内存"Hello Word!"
		 */
//		String greeting = "Hello";
//		greeting = greeting.substring(0, 3) + "p!";
//		//从c++的角度理解上述2句
//		char* greeting = "Hello";
//		//当采用另一个字符串替换 greeting 的时候, Java 代码大致进行下列操作:
//		char* temp = malloc(6);//忽略字符编码规则不同
//		stmcpy(temp, greeting, 3);
//		strncpy(temp + 3, "p!", 3);
//		greeting = temp;
//		//java的String可能是共享内存的,substring一般不复制而是共享原串的部分空间
//		//由垃圾回收机制保障不发生内存泄漏或临界资源访问

String实例

package 第3章_Java的基本程序设计结构;

public class UseString {
	public static void main(String[] args) {
        //1. 创建字符串对象
		String str1 = "";//每个用双引号括起来的字符串都是 String类的一个实例
		str1 = "Hello Word!";
		System.out.println(str1);
		String str2 = "Hello";
		String str3 = str2.substring(0, 3);//[ , )
		System.out.println(str3);
        
		//当将一个字符串与一个非字符串的值进行拼接时,后者被转换成字符串
		int age = 13;
		String rating = "PC" + age;
		System.out.println(rating);
        
		//如果需要把多个字符串放在一起, 用一个定界符分隔,可以使用静态 join 方法:
		String all = String.join(", ", "S", "M", "L", "XL");
		System.out.println(all);

		//检测字符串是否相等
		System.out.println("Hello".equals("Hello"));
		//检测字符串地址是否相同
		System.out.println("Hello" == "Hello");
		/*
		 * 如果虚拟机始终将相同的字符串共享, 就可以使用 == 运算符检测是否相等。但实际上
		 * 只有字符串常量是共享的,而 + 或 substring 等操作产生的结果并不是共享的。
		 * 因此,千万不要使甩 == 运算符测试字符串的相等性, 以免在程序中出现糟糕的 bug。
		 */
        
         //空 串 与 Null 串
		//两种判断空串的方法
		String s1 = "";
		if(s1.length() == 0)
			System.out.println("是空串");
		if(s1.equals(""))
			System.out.println("是空串");
		//null串见第四章
        
         String s2 = "⑪ is the set of octonions";
		//使用 UTF-16 编码表示字符⑪(U+1D546) 需要两个代码单元。调用
		char ch = s2.charAt(0);
		System.out.println(ch);
		/*
		 * 返回的不是一个空格, 而是⑪的第二个代码单元。 
		 * 为了避免这个问题, 不要使用 char 类型。这太底层了
		 */
	}
}

输入流

import java.util.*;

public class UseScanner {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		System.out.print("What is your name?\n");
		String name = sc.nextLine();//读入一整行
		System.out.print("What is your firstname?\n");
		String firstName = sc.next();//读入遇到空格结束
		System.out.printf("name :%s, firstname:%s\n", name, firstName);
	}

}
3.7.2 格式化输出
public class UseFormat {
  public static void main(String[] args) {
  	String name = "盖伦";
  	int kill = 8;
  	String title = "超神";

  	String sentenceFormat = "%s 在进行了连续 %d 次击杀后,获得了 %s 的称号%n";
      //可以使用静态的 String.format 方法创建一个格式化的字符串, 而不打印输出:
      String message = String.format("Hello, %s. Next year, you'll be %d", name, age);
  	/*
  	 * printf和format能够达到一模一样的效果,通过eclipse查看java源代码 可以看到
  	 * 在printf中直接调用了format
  	 */
  	// 使用printf格式化输出
  	System.out.printf(sentenceFormat, name, kill, title);
  	// 使用format格式化输出
  	System.out.format(sentenceFormat, name, kill, title);
  	
  	int year = 2020;
      //总长度,左对齐,补0,千位分隔符,小数点位数,本地化表达
      
      //%n换行可以跨平台
      //直接打印数字
      System.out.format("%d%n",year);
      //总长度是8,默认右对齐
      System.out.format("%8d%n",year);
      //总长度是8,左对齐
      System.out.format("%-8d%n",year);
      //总长度是8,不够补0
      System.out.format("%08d%n",year);
      //千位分隔符
      System.out.format("%,8d%n",year*10000);
      //小数点位数
      System.out.format("%.2f%n",Math.PI);
  }
}
空格与汉字问题
//空格与汉字的问题
         int a = 2, b = 4;
		System.out.printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n");
		//下面四句等价
		System.out.printf("%8c" + "b\n", ' ');
		System.out.printf("\t" +  "c\n");
		System.out.printf("%"+ 2*4 +"c" +"d\n", ' ');
		System.out.printf("%"+ a*b +"c" +"e\n", ' ');
		//这句就很奇怪,它把2个空格压缩成一个,为了与汉字
		System.out.printf("%"+ a*b +"c" +"中\n", ' ');
日期格式化
//日期相关的格式化
		Calendar cur = Calendar.getInstance();//获取但前时间
		cur.set(2020, 5-1, 1);//注意月份是0~11,输入时间是2020-5-1
		//参数索引: 索引值(从1开始)必须紧跟在 % 后面, 并以 $ 终止
		System.out.printf("%1$tY年%1$tm月(月历)\n", cur);
3.7.3 **文件输入与输出 **
3.9 大数值
3.10 数组
一维数组
package 第3章_Java的基本程序设计结构;

import java.util.Arrays;

public class UseArray {
	public static void main(String[] args) {
		// 两种声明方式
		int[] a = new int[10];
		int b[] = new int[10];
		// java的数组等同于C++的数组指针
		// int *a = new int[10];//C++
        
        
		/*
		 * 创建一个数字数组时, 所有元素都初始化为 0
		 * boolean 数组的元素会初始化为 false 
		 * 对象数组的元素则初始化为一个特殊值 null,
		 * 这表示这些元素(还)未存放任何对象
		 */
		String[] names = new String[10];
		// 初始值为null,下面是复制为空
		for (int i = 0; i < 10; i++)
			names[i] = "";
		for (int i = 0; i < a.length; i++)
			System.out.print(a[i] + " ");
		System.out.println();
		// for-each循环
		for (int element : a)
			System.out.print(element + " ");
		System.out.println();
		System.out.println(Arrays.toString(a));
		// 创建数组对象并同时赋予初始值的简化书写形式
		int[] smallPrimes = { 2, 3, 5, 7, 11, 13 };
		// 初始化一个匿名数组来改变数组的值
		smallPrimes = new int[] { 17, 19, 23, 29, 31, 37 };
		// 数组可以赋值
		int[] anonymous = { 17, 19, 23, 29, 31, 37 };
		smallPrimes = anonymous;
		/*
		 * java允许数组长度为 0,在编写一个结果为数组的方法时, 如果碰巧结果为空, 则这种语法形式就显得非常有用。 此时可以创建一个长度为 0 的数组:
		 * new elementType[0] 注意, 数组长度为 0 与 null 不同。
		 */
		// 数组浅拷贝,此时2个数组共同指向一块储存空间
		int[] luckyNumbers = smallPrimes;
		luckyNumbers[5] = 12;// now samllPimes[5] is also 12
		// 为了真正实现拷贝,要使用 Arrays 类的 copyOf方法:
		int[] copiedLuckyNumbers = Arrays.copyOf(luckyNumbers, luckyNumbers.length);
		// 数组排序
		Arrays.sort(a);
	}
}
例题1
package 第3章_Java的基本程序设计结构;

import java.util.Arrays;
import java.util.Scanner;

public class ArrayExample1 {
	public static void main(String[] args) {
		/*
		 * 问题描述:
		 * 假如抽彩是从1~n这n个数中随机抽取 k个,把k个抽中的数升序排序后输出
		 */
		Scanner in = new Scanner(System.in);
		
		System.out.print("How many numbers do you need to draw? ");
		int k = in.nextInt();
		
		System.out.print("What is the highest number you can draw?");
		int n = in.nextInt();
		
		//fill an array with numbers 1 2 3 ... n
		int[] numbers = new int[n];
		for(int i = 0; i < numbers.length; i++)
			numbers[i] = i+1;
		
		//draw k numbers and put them into a second arry
		int[] result = new int[k];
		for(int i = 0; i < result.length; i++)
		{
			//make a random index between 0 and n-1
			int r = (int) (Math.random()*n);//Math.random方法将返回一个 [0, 1)的随机浮点数
			
			//pick the element at the random location
			result[i] = numbers[r];
			
			//一种有效方法:避免在无重复数组中选一个数两次
			// move the last element into the random location
			numbers[n-1] = numbers[r];
			n--;
		}
		
		//print the sorted array
		Arrays.sort(result);
		System.out.println("Bet the following combination. It'll make you rich!");
		for(int r : result)
			System.out.println(r);
	}

}
二维数组
package 第3章_Java的基本程序设计结构;

import java.util.Arrays;

public class UseArray2 {
	public static void main(String[] args) {
		//声明二维数组
		double [][] balances;
		balances = new double [10][6];
		//对应C++的二级指针(下面3句)
//		double **balances = new double*[10];//C++
//		for (i = 0; i < 10; i++)
//			balances[i] = new double [6] ;
		int[][] magicSquare =
			{
			{16, 3, 2, 13},
			{5, 10, 11, 8},
			{9, 6, 7, 12},
			{4, 15, 14, 1}
			};
		//获取长度
		int b = balances.length;
		int bi = balances[0].length;
		
		//遍历二维数组
		for(int[] row : magicSquare)
			for(int value : row)
				System.out.println(value);
		
		System.out.println(Arrays.deepToString(magicSquare));
		
		/* Java 实际上没有多维数组, 只有一维数组。
		 * 多维数组被解释为“数组的数组”
		 * balances 数组实际上是一个包含 6 个元素的数组,
		 * 而每个元素又是一个由 10 个浮点数组成的数组
		 */
		
		//交换二维数组的某两行
		int i = 2;
		double[] temp = balances[i];
		balances[i] = balances[i+1];
		balances[i+1] = temp;
		//构造一个不规则的二维数组
		/*
		1
		1 1
		1 2  1
		1 3  3  1
		1 4  6  4  1
		1 5 10 10  5 1
		1 6 15 20 15 6 1
		*/
		int[][] odds = new int[7][];
		for(int j = 1; j <= 7; j++)
			odds[j] = new int[j];
		//赋值省略
		
	}

}

第四章 对象与类

4.1 面向对象程序设计概述
/*
	 * 结构化程序设计:		算法+数据结构
	 * 面向对象程序设计:	数据结构+算法
	 * 对象的三个特性:
	 * 对象的行为(behavior):	可以对对象施加哪些操作,或可以对对象施加哪些方法?
	 * 对象的状态 (state):		当施加那些方法时,对象如何响应?
	 * 对象标识(identity ):	如何辨别具有相同行为与状态的不同对象?(联系数据库的表)
	 */
4.2 使用预定义类
Employee的实现
import java.time.LocalDate;

/*
 * C++方法的实现如果在类中,这个方法将自动地成为内联(inline) 方法
 * 在 Java 中, 所有的方法都必须在类的内部定义, 但并不表示它们是内联方法。 
 */
//在一个源文件中, 只能有一个公有类,但可以有任意数目的非公有类。
public class EmployeeTest {
	public static void main(String[] args) {
		//fill the staff array with three Employee objects
		Employee[] staff = new Employee[3];
		
		staff[0] = new Employee("Carl Cracker", 75000, 1987, 12, 15);
		staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
		staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 15);
		
		//raise  everyone's salary by 5%
		for(Employee e : staff)
			e.raiseSalary(5);
		
		//print out information about all Employee objects
		for(Employee e: staff)
			System.out.println("name = " + e.getName() + ","
					+ " salay = " + e.getSalary() + ", hi reDay=" + e.getHireDay());
	}

}

class Employee
{
	// instance fields(实例域),强烈建议将实例域标记为 private保证封装性
	private String name;
	private double salary;
	private LocalDate hireDay;
	
	// constructor(构造器)
	public Employee(String nameIn, double salaryIn, int yearIn, int monthIn, int dayIn)
	{
		this.name = nameIn;
		this.salary = salaryIn;
		this.hireDay = LocalDate.of(yearIn, monthIn, dayIn);
	}
	
	// method(方法)
	public String getName()
	{
		return name;
	}
	
	public double getSalary()
	{
		return salary;
	}
	
	public LocalDate getHireDay()
	{
//		注意不要编写返回引用可变对象的访问器方法。
//		因为可变对象有更改器(mutator method),会导致更改器方法可以改变对象的私有状态!
		return hireDay;//OK因为LocalDate没有更改器,但Date就不行
//		return (Date) hireDay.clone(); //OK
	}
	
//	 关键字 this表示隐式参数(implicit), byPercent是显示参数(explicit)
	public void raiseSalary(double byPercent)
	{
		double raise = salary*byPercent/100;
		salary += raise;
	}
}
4.2.1 java类库中的Date类
import java.time.LocalDate;
import java.util.Date;

public class ObjectOrientedProgramming {
	public static void main(String[] args) {
//		如何构造一个对象:使用构造器
		new Date();
		System.out.println(new Date());//可以将对象传递给一个方法
		String s = new Date().toString();//如何将 toString方法应用于新构造的Date对象上
		Date birthday = new Date();//birthday这个变量引用了Date对象
//		Date* birthday = new Date(); // C++
		Date deadline = birthday;//两个变量引用同一个对象
//		可以显式地将对象变量设置为 null, 表明这个对象变量目前没有引用任何对象
//		如果将一个方法应用于一个值为 null 的对象上,那么就会产生运行时错误。
//		对象(局部变量)不会自动地初始化为 null,而必须通过调用 new 或将它们设置为 null 进行初始化。
		deadline = null;
		if (deadline != null)//dead code(废话)
			System.out.println(deadline);
        }
}
4.2.2 Java 类库中的 LocalDate 类
import java.time.LocalDate;
import java.util.Date;

public class ObjectOrientedProgramming {
	public static void main(String[] args) {
//		类库设计者决定将保存时间与给时间点命名分开。所以标准 Java 类库分别包含了两个类:
//		一个是用来表示时间点的Date类;另一个是用来表示大家熟悉的日历表示法的LocalDate类
//		不要使用构造器来构造 LocalDate 类的对象。
//		应当使用静态工厂方法 (factory method) 代表你调用构造器。()
		System.out.println(LocalDate.now());
		System.out.println(LocalDate.of(2020, 5, 26));
		LocalDate newYearsEve = LocalDate.of(1999, 12, 31);
		int year = newYearsEve.getYear(); // 1999
		int month = newYearsEve.getMonthValue(); // 12
		int day = newYearsEve.getDayOfMonth(); // 31
		//通过LocalDate快速算出1000天后的年月日
		LocalDate aThousandDaysLater = newYearsEve.plusDays(1000);
//		只访问对象而不修改对象法:称为访问器方法(acessor method),newYearsEve的值并不会改变
//		方法会改变对象的状态称为:更改器方法 ( mutator method ) 
//		对与C++可以通过const后缀区分,但java不能
		year = aThousandDaysLater.getYear();// 2002
		month = aThousandDaysLater.getMonthValue(); // 09
		day = aThousandDaysLater.getDayOfMonth(); // 26
	}

}
4.3 用户自定义的类
4.3.9 final 实例域
class Employee
{
	private final String name;
}
final 修饰符大都应用于基本 (primitive) 类型域,或不可变(immutable) 类的域(如果类
中的每个方法都不会改变其对象, 这种类就是不可变的类。 例如,String类就是一个不可变
的类 )。
对于可变的类,使用 final 修饰符可能会对读者造成混乱。

例子

private final StringBuiIcier evaluations;
//在 Employee 构造器中会初始化为
evaluations = new StringBuilder();
//final 关键字只是表示存储在 evaluations 变量中的对象引用不会再指示其他 StringBuilder对象。
//不过这个对象可以更改:
public void giveGoldStarO
{
	evaluations.append(LocalDate.now() + ": Gold star!\n");
}
4.4 静态域与静态方法
4 . 4.1 静态域 (类域)
class Employee
{
    private static int nextld = 1;
    private int id;
    ...
}
4.4.2 静态常量

最好不要将域设计为 public。然而, 公有常量(即 final 域)却没问题。

public class Math
{
    ...
	public static final double PI = 3.14159265358979323846;
    //可以采用 Math.PI 的形式获得这个常量。
    ...
}
另一个多次使用的静态常量是 System.out。它在 System 类中声明:
public class System
{
	public static final PrintStream out = . . .;
    System.out = new PrintStrean(. . .); // Error out is final
}
4.4.3 静态方法

Math.pow(x, a)在运算时,不需要用任何 Math 对象
我们建议使用类名, 而不是对象来调用静态方法。
main方法也是静态方法

**每一个类可以有一个 main 方法。**这是一个常用于对类进行单元测试的技巧。例
如,可以在 Employee 类中添加一个 main 方法: 右击mian方法运行

在下面2种情况下使用静态方法:

  • 一 方法不需要访问对象状态,其所需参数都是通过显式参数提供(例如: Math.pow )
  • 一个方法只需要访问类的静态域(例如:Employee.getNextldh)
4.4.4 工厂方法

静态方法还有另外一种常见的用途。类似LocalDateNumberFormat的类
使用**静态工厂方法 **(factory methoc) 来构造对象。 你已经见过工厂方法 LocalDate.nowLocalDate.of
NumberFormat类如下使用工厂方法生成不同风格的格式化对象:

NumberFormat currencyFormatter = NumberFormat.getCurrencylnstance();
NumberFormat percentFormatter = NumberFormat.getPercentlnstance();
double x = 0.1;
System.out.println(currencyFormatter.format(x)); // prints $O.10
System.out.println(percentFomatter.format(x)); // prints 10%

为什么 NumberFormat 类不利用构造器完成这些操作呢? 这主要有两个原因:

  • 无法命名构造器。构造器的名字必须与类名相同。但是, 这里希望将得到的货币实例
    和百分比实例采用不用的名字。
  • 当使用构造器时,无法改变所构造的对象类型。而 Factory方法将返回一个 DecimalFormat
    类对象, 这是 NumberFormat 的子类(有关继承的详细内容请参看第 5 章)
4.5 方法参数

按值调用 (call by value)
按引用调用 ( call by reference)

Java 程序设计语言总是采用按值调用。也就是说, 方法得到的是所有参数值的一个拷
贝,特别是,方法不能修改传递给它的任何参数变量的内容

public static void tripieValue(double x) // doesn't work
{
	x = 3 * x;
}
double percent = 10;
tripieValue(percent);
说明java没有call by reference
import java.time.LocalDate;

public class Main {
	public static void main(String[] args) {
		Employee[] staff = new Employee[2];

		staff[0] = new Employee("Carl Cracker", 75000, 1987, 12, 15);
		staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
        
		Employee.swap(staff[0], staff[1]);
		for (Employee e : staff)
			System.out.println(e.getName());//交换失败
	}
}

class Employee {
	// instance fields(实例域)
	private String name;
	private double salary;
	private LocalDate hireDay;

	// constructor(构造器)
	public Employee(String nameIn, double salaryIn, int yearIn, int monthIn, int dayIn) {
		this.name = nameIn;
		this.salary = salaryIn;
		this.hireDay = LocalDate.of(yearIn, monthIn, dayIn);
	}

	// method(方法)
	public String getName() {
		return name;
	}

	public double getSalary() {
		return salary;
	}

	public LocalDate getHireDay() {
		return hireDay;// OK因为LocalDate没有更改器,但Date就不行
	}

	public void raiseSalary(double byPercent) {
		double raise = salary * byPercent / 100;
		salary += raise;
	}

	public static void swap(Employee x, Employee y) // doesn't work
	{
        //    可以类比C++
        //    swap(Employee* x, Employee* y)
        //    {
        //        Employee t = x;
        //        x = y;
        //        y = t;
        //    }
		Employee temp = x;
		x = y;
		y = temp;
	}
}

下面总结一下 Java 中方法参数的使用情况:

  • 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)。
  • 一个方法可以改变一个对象参数的状态。
  • 一个方法不能让对象参数引用一个新的对象。

C+ + 注释: C++ 有值调用和引用调用。 引用参数标有 & 符号。 例如, 可以轻松地实现
void tripleValue(double& x) 方法或 void swap(Employee& x, Employee& y) 方法实现修改
它们的引用参数的目的。

4.6 对 象 构 造
4.6.1 重载
//构造器方法重载
StringBuilder messages = new StringBuilder();
StringBuilder todoList = new StringBuilder("To do:\n");

编译器必须挑选出具体执行哪个方法,它通过用各个方法给出的参数类型与特定方法调用所使用的值类型进行匹配来挑选出相应的方法。 如果编译器找不到匹配的参数, 就会产生编译时错误, 因为根本不存在匹配, 或者没有一个比其他的更好。(这个过程被称为重载解析(overloading resolution)。)

//一般方法重载
//要完整地描述一个方法,需要指出方法名以及参数类型。这叫做方法的签名(signature)
//返回类型不是方法签名的一部分。不能有两个名字相同、 参数类型也相同却返回不同类型值的方法。
//String 类有4个称为 indexOf 的公有方法。 它们的签名是
indexOf(int)
indexOf(int, int)
indexOf(String)
indexOf(String, int)
4.6.2 默认域初始化

如果在构造器中没有显式地给域赋予初值,那么就会被自动地赋为默认值:
数值为 0、布尔值为 false、 对象引用为 null。 (建议要给域赋初值)

这是域与局部变量的主要不同点。 必须明确地初始化方法中的局部变量。 但是,
如果没有初始化类中的域, 将会被自动初始化为默认值( 0、 false 或 null )。

4.6.3 无参数的构造器

很多类都包含一个无参数的构造函数, 对象由无参数构造函数创建时, 其状态会设置为
适当的默认值。 例如, 以下是 Employee 类的无参数构造函数:

public Employee()
{
	name = "";
	salary = 0;
	hireDay = LocalDate.now();
} 
//也可以写成下面这样
public Employee()
{
//这个构造器将所有的实例域设置为默认值。
//实例域中的数值型数据设置为 0、 布尔型数据设置为 false、 所有对象变量将设置为 null。
}

如果在编写一个类时没有编写构造器, 那么系统就会提供一个无参数构造器。这个构造
器将所有的实例域设置为默认值。于是, 实例域中的数值型数据设置为 0、 布尔型数据设置
为 false、 所有对象变量将设置为 null。

如果类中提供了至少一个构造器, 但是没有提供无参数的构造器, 则在构造对象时如果
没有提供参数就会被视为不合法。 例如, 在程序清单 4-2 中的 Employee 类提供了一个简单
的构造器:
Employee(String name, double salary, int y, int ra, int d) 对于这个类,构造默认的雇员属于不合法。也就是, 调用
e = new Eraployee() ; 将会产生错误。

请记住, 仅当类没有提供任何构造器的时候, 系统才会提供一个默认的构造器

4.6.4 显式域初始化

在执行构造器之前,先执行赋值操作。当一个类的所有构造器都希望把相同的值赋予某
个特定的实例域时,这种方式特别有用。
初始值不一定是常量值。在下面的例子中, 可以调用方法对域进行初始化。仔细看一下
Employee 类,其中每个雇员有一个 id 域。可以使用下列方式进行初始化:

class Employee
{
	private String name = "";//常量
}
class Employee
{
	private static int nextld;
	private int id = assignld();//变量
	private static int assignld()
	{
		int r = nextld;
		nextld++;  
	}
}

C++注释

在 C++ 中, 不能直接初始化类的实例域。 所有的域必须在构造器中设置。 
 但是, 有一个特殊的初始化器列表语法,如下所示:
Employee::Employee(String n,double s, int y, int m, int d) // C++
: name(n),
salary(s),
hireDay(yf nt d)
{}
C++ 使用这种特殊的语法来调用域构造器。在 Java 中没有这种必要, 因为对象没有
子对象, 只有指向其他对象的指针。
4.6.5 参数名

this.name = name;name = nameIn

4.6.6 调用另一个构造器

如果构造器的第一个语句形如 this(…), 这个构造器将调用同一个类的另一个构造器。

public Employee(double s)
{
    // calls Employee(String, double)
    this("Employee #" + nextld, s);
    nextld++;
}

当调用 new Employee(60000)时, Employee(double)构造器将调用Employee(String,double) 构造器。
采用这种方式使用 this关键字非常有用, 这样对公共的构造器代码部分只编写一次即可。

4.6.7 初始化块
class Employee
{
    private static int nextld;
    private int id;
    private String name;
    private double salary;
// object initialization block只要构造类的对象,这些块就会被执行。
    {
        id = nextld;
        nextld++;
    }
    public Employee(String n, double s)
    {
        name = n;
        salary = s;
    }

    public Employee()
    {
    name = "";
    salary = 0;
    }
}

由于初始化数据域有多种途径,所以列出构造过程的所有路径可能相当混乱。下面是调
用构造器的具体处理步骤:

  1. 所有数据域被初始化为默认值(0、 false 或 null。)
  2. 按照在类声明中出现的次序, 依次执行所有域初始化语句和初始化块。
  3. 如果构造器第一行调用了第二个构造器, 则执行第二个构造器主体
  4. 执行这个构造器的主体.

要避免让类的构造器行为依赖于数据域声明的顺序, 那就会显得很奇怪并且容易引起错误。

可以通过提供一个初始化值, 或者使用一个静态的初始化块来对静态域进行初始化。前
面已经介绍过第一种机制:
private static int nextld = 1; 如果对类的静态域进行初始化的代码比较复杂,那么可以使用静态的初始化块。
将代码放在一个块中,并标记关键字 static。下面是一个示例。其功能是将雇员 ID 的起
始值赋予一个小于 10 000 的随机整数。

// static initialization block
static
{
    Random generator = new Random();
    nextld = generator.nextlnt(lOOOO);
}

在类第一次加载的时候, 将会进行静态域的初始化。与实例域一样,除非将它们显式地
设置成其他值, 否则默认的初始值是 0、 false 或 null。 所有的静态初始化语句以及静态初始
化块都将依照类定义的顺序执行。

对象构造实例
  • 重载构造器
  • 用 this(…) 调用另一个构造器
  • 无参数构造器
  • 对象初始化块
  • 静态初始化块
  • 实例域初始化
import java.util.*;

public class Main
{
    public static void main(String[] args)
    {
        // fill the staff array with three Employee objects
        Employee[] staff = new Employee[3];
        staff[0] = new Employee("Harry", 40000);
        staff[1] = new Employee(60000);
        staff[2] = new Employee();
        // print out information about all Employee objects
        for (Employee e : staff)
            System.out.println("name=" + e.getName() + ",id=" + e.getId() + ",salary:" + e.getSalary());
        
    }
}
class Employee
{
    private static int nextId;
    
    private int id;
    private String name = "";//instance field initilalization
    private double salary;
    
    //static initialization block
    static
    {
        Random generator = new Random();
        //set nextId to random nummber between 0 and 999
        nextId = generator.nextInt(1000);
    }
    
    //object initialization block
    {
        id = nextId;
        nextId++;
    }
    
    // three overloaded constructors
    public Employee(String n, double s)
    {
        name = n;
        salary = s;
    }
    
    public Employee(double s)
    {
        //call the Employee(String, double) constructor
        this("Employee #" + nextId, s);//nextId会因为+自动转成字符类型
    }
    
    public Employee()
    {
        // name initialized to "" see above
        // salary not explicitly set initialized to 0
        // id initialized in initialization block
    }
    
    public String getName()
    {
        return name;
    }
        
    public double getSalary()
    {
        return salary;
    }
    
    public int getId()
    {
        return id;
    }   
}
4.7 四种访问权限
public			对所有类可见
protected 		对本包和所有子类可见
无修饰符		对本包可见
private		   仅对本类可见
final			不可继承
static			静态
对于类只能用public,无修饰符,static和final四种
private: 修饰外部类就失去了定义这个类的用途了
protected: 子类和包可见,但是包外不能访问又何来子类,所以也不能作用在类上