第三章 类基础知识
1 Java类结构和main函数
1.1 Class 类
- Java文件必须以
.java作为扩展名 - 一个Java文件只能有一个
public class public class的名字必须和文件名字完全一样- 一个文件可以有多个
class,但只能有一个是public
public class IntegerTest
{
//此文件必须命名为IntegerTest.java
public static void main(String[] args)
{
//PSVM, main的必备缩写
int a, b, c;
a = 1; b = 2;
c = a+b;
System.out.println("Hello World");
}
}
1.2 main函数
-
一个class最多只能有一个main函数。类可以没有main函数,没有main函数的类就不能主动执行,但可以被动执行(被别人调用执行)。
-
程序的入口都是main函数,这点和C/C++一致。有一点不同的是,Java要求所有程序都必须放在类对象中,即所有的代码都要放在class的所辖范围内。
-
main函数的形参和前缀修饰符public、static、void等都不能省略,是固定写法。可简单记忆为PSVM。
-
main函数无法被其他方法/类所调用。
-
一个Java程序可以调用多个其他Java class。
-
String[] args是main函数的形参,即在main函数中可以使用args的值,这些值实在main函数启动的时候输入的public class ArgumentTest { public static void main(String[] args) { for(int i=0;i<args.length;i++) { //依次将形参输出 System.out.println(args[i]); } } }
2 Java基本类型和运算符
2.1 基本类型
-
boolean布尔
只有true或false两种值,默认falsepublic class BooleanTest { public static void main(String[] args) { boolean a = true; //not TRUE True boolean b = 5<3; //false; a = false; // a = 1; error } } -
byte字节- 1 byte = 8 bits(8位)
- 最小值-128,最大值127
-
short/int/long- 16位/32位/64位
- 默认值0/0/0L
public class MAXNUM {
public static void main(String[] args) {
byte a = Byte.MAX_VALUE;
short b = Short.MAX_VALUE;
int c = Integer.MAX_VALUE;
long d = Long.MAX_VALUE;
float e = Float.MAX_VALUE;
double f = Double.MAX_VALUE;
System.out.println("Byte.MAX_VALUE = " + a);
System.out.println("Short.MAX_VALUE = " + b);
System.out.println("Integer.MAX_VALUE = " + c);
System.out.println("Long.MAX_VALUE = " + d);
System.out.println("Float.MAX_VALUE = " + e);
System.out.println("Double.MAX_VALUE = " + f);
}
}
-
float/double- 32位/64位
- 默认值0.0f/0.0d
public class FloatingTest { public static void main(String[] args) { float f1 = 1.23f; // float f2 = 1.23; error, float赋值必须带f double d1 = 4.56d; double d2 = 4.56; //double 可以省略末尾d System.out.println(f1); //1.23 System.out.println((double)f1); //转换到double, 输出1.2300000190734863 System.out.println(d1); //4.56 System.out.println((float)d2); //4.56 System.out.println(f1==1.229999999f); //true System.out.println(f1-1.229999999f); //0.0 System.out.println(d2==4.559999999999999999d); //true System.out.println(d2-4.559999999999999999d); //0.0 } } -
char
最小值\u0000,最大值\uffff
3 选择和循环结构
同c语言
public class IfElseTest {
public static void main(String[] args) {
int a = 5;
if(a>1){
System.out.println("aaaaaaa");
}
if(a>10){
System.out.println("bbbbbbb");
}else{
System.out.println("ccccccc");
}
if(a>10){
System.out.println("dddddd");
}else if(a>=5){
System.out.println("eeeeee");
}else{
System.out.println("ffffff");
}
}
}
public class SwitchTest {
public static void main(String[] args) {
int a1 = 1;
int a2 = 2;
switch(a1+a2){
case 1: System.out.println("aaaaaaa");
break;
case 2: System.out.println("bbbbbbb");
break;
case 3: System.out.println("ccccccc");
//break;
default:System.out.println("ddddddd");
}
String b = "abc";
switch(b){
case "abc": System.out.println("eeeeeee");
break;
case "def": System.out.println("fffffff");
break;
case "hgi": System.out.println("ggggggg");
break;
default:System.out.println("hhhhhhh");
}
}
}
public class ForTest {
public static void main(String[] args) {
for(int i=0;i<5;i++){
for(int j=0;j<5;j++){
if(j<=i){
System.out.print("*");
}else{
break;
}
}
System.out.println();
}
}
}
public class WhileTest {
public static void main(String[] args) {
System.out.println("=============While Test==========");
int x = 10;
while (x < 20) {
System.out.print("value of x : " + x);
x++;
System.out.print("\n");
}
System.out.println("=============Do While Test==========");
x = 10;
do {
x++;
if(x%2 == 0)
{
continue;
}
System.out.println("value of x : " + x);
} while (x < 20);
}
}
4 自定义函数
4.1 简介
- 函数必须放在类的范围内
- 格式:
修饰词 返回值 函数名(形参列表){函数体}- 修饰词:
public或static,通常建议public - 返回值:
void、int等
- 修饰词:
- 函数中可调用其他函数
public class FactorialTest {
public static void main(String[] args) {
int a = 5;
int b = factorialCalculation(a);
System.out.println("The factorial of " + a + " is " + b);
}
public static int factorialCalculation(int m) {
if (m > 1) {
return m * factorialCalculation(m - 1);
} else {
return 1;
}
}
}
4.2 重载函数(overload)
- 同一个类中,函数名称可以相同,即重载函数(overload),但函数参数的个数或者类型必须有所不同
- 不能以返回值来区分同名函数
public class OverloadTest {
public static void main(String[] args) {
int a=1,b=2;
System.out.println(add(1,2));
System.out.println(add(1.5,2.5));
}
public static int add(int m, int n)
{
return m + n;
}
public static double add(double m, double n) //和第9行行数重载overload
{
return m + n;
}
/*
* 以下函数非法,和第9行的函数名相同,形参相同
public static double add(int m, int n)
{
return (double) (m + n);
}
*/
}
第四章 面向对象和类
1 面向对象思想
2 java类和对象
- 格式:
A obj = new(A);obj相当于 C/C++中的指针,在Java中称为Reference- 对象赋值是Reference赋值,而基本类型是直接值拷贝(因为基本类型变量值小,可直接拷贝,对象包含多个值,不容易复制)
- 类成员变量有初始值,函数临时变量必须初始化
byte:0 short:0 int:0 long:0Lfloat:0.0f double:0.0dboolean:false char:'\u0000'
例1:对象赋值是Reference赋值
public class ReferenceTest {
public static void main(String[] args) {
int num1 = 5;
int num2 = num1;
System.out.println("num1: " + num1 + ", num2: " + num2);
num2 = 10;
System.out.println("num1: " + num1 + ", num2: " + num2);
MyNumber obj1 = new MyNumber();
MyNumber obj2 = new MyNumber();
System.out.println(obj1.num);
System.out.println(obj2.num);
System.out.println("======接下来输出新值=====");
obj2 = obj1; // 对象赋值是Reference赋值
obj2.num = 10;
System.out.println(obj1.num);
System.out.println(obj2.num);
}
}
class MyNumber {
int num = 5;
}
【输出】
num1: 5, num2: 5
num1: 5, num2: 10
5
5
======接下来输出新值=====
10
10
例2:函数中理解对象赋值是Reference赋值,而基本类型是直接值拷贝
public class other {
public static void main(String[] args) {
int a = 1, b = 2;
swap(a,b);
System.out.println("a is " + a + ", b is " + b); //a=1, b=2
MyNumber obj1 = new MyNumber();
MyNumber obj2 = new MyNumber();
obj2.num = 10;
swap(obj1, obj2);
System.out.println("obj1 is " + obj1.num + ", obj2 is " + obj2.num); // obj1 10, obj2 5
}
public static void swap(int m, int n){
// 函数调用后内存里有a b m n,m和n交换了但a和b没交换
int s = m;
m = n;
n = s;
}
public static void swap(MyNumber obj3, MyNumber obj4){
// 函数调用后obj3和obj1指向同一个地方,obj4和obj2指向同一个地方
int s = obj3.num;
obj3.num = obj4.num;
obj4.num = s;
}
}
class MyNumber {
int num = 5;
}
【输出】
a is 1, b is 2
obj1 is 10, obj2 is 5
3 构造函数
- Java构造函数的名称必须和类名一样,且没有返回值
- Java具有内存自动回收机制,当变量退出其生命周期后,JVM会自动回收所分配的对象的内存,所以不需要析构函数来释放内存。
- 对象回收效率依赖于垃圾回收器GC(Garbage Collector),其回收算法关系到性能好坏,是JVM的研究热点
- 如果没有显式定义构造函数,Java编译器自动为该类产生一个空的无形参构造函数。
- 每个子类的构造函数的第一句话都默认调用父类的无参数构造函数
super(),除非子类的构造函数第一句话是super,而且super语句必须放在第一条,不会出现连续两条super语句 - 一个类可以有多个构造函数,只要形参列表不相同即可
4 信息隐藏和this
- 信息隐藏:通过类的方法来间接访问类的属性,而不是直接访问类的属性
this- 指向本类中的成员变量
this.a - 指向本类中的成员方法
this.add - 当做构造函数使用
- 指向本类中的成员变量
public GouZao(int m){
this(m, 0);
}
public GouZao(int m, int n){
this.m = m;
this.n = n;
}
第五章 继承、接口和抽象
1 继承
-
父类(Parent class) = 基类(Base class) = 超类(Super class)
-
子类(Child class) = 派生类(Derived class)
-
子类继承父类所有的属性和方法(但不能直接访问
private成员)
可以通过调用父类的方法来访问父类私有的成员属性
// Base.java
public class Base {
private int num = 10;
public int getNum(){
return num;
}
}
// Derived.java
public class Derived extends Base {
public static void main(String[] args) {
Derived d = new Derived();
// System.out.println(d.num); // 报错:'num' 在 'Base' 中具有 private 访问权限
System.out.println(d.getNum()); // 输出:10
}
}
- 在同样方法名和参数情况下,本类的方法会比父类的方法优先级高
public class Base {
private int num = 10;
public int getNum(){
return num;
}
}
public class Derived extends Base {
private int num = 20;
public int getNum(){
return num;
}
public static void main(String[] args) {
Derived d = new Derived();
System.out.println(d.getNum()); // 输出:20
}
}
-
每个类都只能继承一个类
-
如果不写
extends,默认继承java.lang.Object类(包含cloneequalsfinalizegetClasshashCodetoString等方法) -
每个子类的构造函数的第一句话都默认调用父类的无参数构造函数
super(),除非子类的构造函数第一句话是super,而且super语句必须放在第一条,不会出现连续两条super语句
// A.java
public class A{
public A(){
System.out.println("111111");
}
public A(int a){
System.out.println("222222");
}
}
public class B extends A {
public B(){
System.out.println("333333");
}
public B(int a){
System.out.println("444444");
}
public static void main(String[] args) {
B obj1 = new B();
System.out.println("============");
B obj2 = new B(10);
}
}
【输出】
111111
333333
============
111111
444444
public class C extends A {
public C(){
System.out.println("333333");
}
public C(int a){
super(a);
System.out.println("444444");
}
public static void main(String[] args) {
C obj1 = new C();
System.out.println("============");
C obj2 = new C(10);
}
}
【输出】
111111
333333
============
222222
444444
2 抽象类和接口
2.1 抽象类
- 抽象类:只有方法名字、形参列表,没有方法体的方法所在的类
- 用
abstract声明 - 组成
- 成员变量,个数不限
- 具体方法,个数不限
- 抽象方法,加
abstract关键字,个数不限
- 子类可以继承
extends于抽象类,但一定要实现父类的所有abstract方法,如果不能完全实现,子类也必须被定义为抽象类
- 用
public abstract class Shape{
int area;
public abstract void calArea();
}
2.2 接口
- 如果类的所有方法都没实现,那么这个类就算是接口
interface - 接口不算类
- 接口可以有变量,但一般为常量
- 类与接口:类只可以继承
extends一个类,但是可以实现implements多个接口,在同时有继承和实现时,extends应该在implements前面。类实现接口时需要实现所有方法,否则为抽象类。 - 接口与接口:接口可以继承
extends多个接口。 - 抽象类和接口的区别
- 抽象类有构造函数,接口没有
- 抽象类可以有main函数,能运行,接口没有main函数,不能运行
- 抽象类方法可以有
publicprotected,接口方法都是public
public interface Animal{
public void eat();
public void move();
}
3 转型、多态和契约设计
3.1 转型
- 类型可以相互转型,但只限制于有继承关系的类
- 子类可以转化成父类
- 父类不可以转化为子类,除非父类本身是从子类转化过来的
Human obj1 = new Man(); // OK, Man extends Human
Man obj2 = new Human(); // illegal, Man is a derived class Human
Man obj3 = (Man) obj1; // OK, because obj1 is born from Man class
3.2 多态
- 类型转化带来的作用就是多态
- 重写overwrite/override:子类继承父类的所有方法,但子类可以重新定义一个名字、参数和父类一样的方法,这种行为就是重写overwrite/override
- 子类方法的优先级高于父类方法
public class Man extends Human {
public void eat() {
System.out.println("eat");
}
public void plough() {}
public static void main(String[] args) {
Man obj1 = new Man();
obj1.eat(); // call Man.eat()
Human obj2 = (Human) obj1;
obj2.eat(); // call Man.eat()
Man obj3 = (Man) obj1;
obj3.eat(); // call Man.eat()
}
}
- 多态的作用
- 以统一的接口来操纵某一类中不同的对象的动态行为
- 对象之间的解耦
public interface Animal {
public void eat();
public void move();
}
public class Cat implements Animal{
public void eat() {
System.out.println("Cat: I can eat");
}
public void move(){
System.out.println("Cat: I can move");
}
}
public class Dog implements Animal{
public void eat() {
System.out.println("Dog: I can eat");
}
public void move() {
System.out.println("Dog: I can move");
}
}
public class AnimalTest {
public static void haveLunch(Animal a) {
a.eat();
}
public static void main(String[] args) {
Animal[] as = new Animal[4];
as[0] = new Cat();
as[1] = new Dog();
as[2] = new Cat();
as[3] = new Dog();
// 以统一的接口来操纵某一类中不同的对象的动态行为
for(int i=0;i<as.length;i++) {
as[i].move(); // 调用每个元素自身的move方法
}
// 解耦
for(int i=0;i<as.length;i++) {
haveLunch(as[i]);
}
haveLunch(new Cat()); //Animal a = new Cat(); haveLunch(a);
haveLunch(new Dog());
haveLunch( // new接口的同时将接口中所有的方法实现补全
new Animal(){ // 相当于定义了一个匿名类,并在这个类中对Animal进行了实现
public void eat() {
System.out.println("I can eat from an anonymous class");
}
public void move() {
System.out.println("I can move from an anonymous class");
}
}
);
}
}
【输出】
Cat: I can move
Dog: I can move
Cat: I can move
Dog: I can move
Cat: I can eat
Dog: I can eat
Cat: I can eat
Dog: I can eat
Cat: I can eat
Dog: I can eat
I can eat from an anonymous class
3.3 契约设计
-
契约设计就像签订一份合同:在Java中,契约设计就像签订一份合同,接口是合同的条款,实现类是合同的执行者。接口定义了一组方法(包括方法的名称、参数和返回值),实现类需要按照这些方法的要求去实现功能。这样,当其他代码调用这些方法时,它们可以确信实现类会按照契约的要求去执行——成功地将调用类和被调用类解耦(decoupling)。
-
被调用类
public static void haveLunch(Animal a) {
a.eat();
}
- 调用类
haveLunch(new Cat()); //Animal a = new Cat(); haveLunch(a);
haveLunch(new Dog());
haveLunch( // new接口的同时将接口中所有的方法实现补全
new Animal(){ // 相当于定义了一个匿名类,并在这个类中对Animal进行了实现
public void eat() {
System.out.println("I can eat from an anonymous class");
}
public void move() {
System.out.println("I can move from an anonymous class");
}
}
);
第六章 static、final和常量设计
1 static
static可用在变量/方法/类/匿名代码块
1.1 静态变量
- 是类的共有成员,只依赖于类存在,可以通过类访问
- 所有的对象实例关于静态变量的值都共享存储在一个共同的空间
public class Potato {
static int price = 5;
String content = "";
public Potato(int price, String content){
this.price = price;
this.content = content;
}
public static void main(String[] a){
System.out.println(Potato.price); //Potato.content wrong
System.out.println("----------------------------------");
Potato obj1 = new Potato(10,"青椒土豆丝");
System.out.println(Potato.price);
System.out.println(obj1.price);
System.out.println("----------------------------------");
Potato obj2 = new Potato(20,"酸辣土豆丝");
System.out.println(Potato.price);
System.out.println(obj2.price);
}
}
【输出】
5
----------------------------------
10
10
----------------------------------
20
20
1.2 静态方法
- 静态方法无需通过对象来引用,可以通过类名直接引用
- 静态方法中只能使用静态变量,不能使用非静态变量
- 静态方法禁止引用非静态方法
public class StaticMethodTest {
int a = 111111;
static int b = 222222;
public static void hello(){
System.out.println("000000");
System.out.println(b);
//System.out.println(a); //error, cannot call non-static variables
//hi() //error, cannot call non-static method
}
public void hi(){
System.out.println("333333");
hello(); //ok, call static methods
System.out.println(a); //ok, call non-static variables
System.out.println(b); //ok, call static variables
}
public static void main(String[] a){
StaticMethodTest.hello();
//StaticMethodTest.hi(); //error, 不能使用类名来引用非静态方法
StaticMethodTest foo = new StaticMethodTest();
foo.hello(); //warning, but it is ok
foo.hi(); //right
}
}
1.3 static块
- 只在类第一次被加载时调用,即在程序运行期间,这段代码只运行一次
- 执行顺序:static块>匿名块>构造函数
class StaticBlock {
//static block > anonymous block > constructor function
static {
System.out.println("static block 1");
}
{
System.out.println("anonymous block 1");
}
public StaticBlock() {
System.out.println("constructor function");
}
static {
System.out.println("static block 2");
}
{
System.out.println("anonymous block 2");
}
}
public class StaticBlockTest {
public static void main(String[] args) {
StaticBlock obj1 = new StaticBlock();
System.out.println("------------------");
StaticBlock obj2 = new StaticBlock();
}
}
【输出】
static block 1
static block 2
anonymous block 1
anonymous block 2
constructor function
------------------
anonymous block 1
anonymous block 2
constructor function
2 单例模式
- 单例模式(Singleton):限定某一个类在整个程序运行过程中只能保留一个实例对象在内存空间
- 采用
static来共享对象实例 - 采用
private构造函数,防止外界new操作
- 采用
public class Singleton {
private static Singleton obj = new Singleton(); //共享同一个对象
private String content;
private Singleton(){ //确保只能在类内部调用构造函数
this.content = "abc";
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public static Singleton getInstance() {
//静态方法使用静态变量
//另外可以使用方法内的临时变量,但不能引用非静态变量
return obj;
}
public static void main(String[] args) {
Singleton obj1 = Singleton.getInstance();
System.out.println(obj1.getContent()); //abc
Singleton obj2 = Singleton.getInstance();
System.out.println(obj2.getContent()); //abc
obj2.setContent("def");
System.out.println(obj1.getContent());
System.out.println(obj2.getContent());
System.out.println(obj1 == obj2); //true, obj1和obj2指向同一个对象
}
}
【输出】
abc
abc
def
def
true
3 final
- 可以用来修饰类、方法、字段
final的类不能被继承
final public class FinalFather {
}
class Son1 extends FinalFather{
}
- 父类中的
final的方法不能在子类中被重写
public class FinalMethodFather {
public final void f1(){
}
}
public class FinalMethodSon extends FinalMethodFather{
public void f1(){
}
}
final的变量不能被再次赋值- 如果是基本类型的变量,不能修改其值
- 如果是对象实例,不能修改其指针(但可以修改对象内部的值)
// 如果是基本类型的变量,不能修改其值
public class FinalPrimitiveType {
public static void main(String[] args) {
final int a = 5;
a=10; // wrong, 不能修改其值
}
}
// 如果是对象实例,不能修改其指针(但可以修改对象内部的值)
class FinalObject{
int a = 10;
}
public class FinalObjectTest {
public static void main(String[] args) {
final FinalObject obj1 = new FinalObject();
System.out.println(obj1.a);
obj1.a = 20; // OK, 可以修改对象内部的值
System.out.println(obj1.a);
obj1 = new FinalObject(); // wrong, 不能修改其指针
}
}
4 常量设计和常量池
==用于比较基本数据类型时,会比较两个值的数值是否相等。==用于比较引用类型时,会比较两个引用是否指向同一个对象。
4.1 常量设计
- 一般的常量:
public static final,变量名全大写,以连字符相连- 不能修改:用
final - 不会修改/只读/只要一份:用
static - 方便访问:用
public
- 不能修改:用
public class Constants {
public final static double PI_NUMBER = 3.14;
public static final String DEFAULT_COUNTRY="China";
public static void main(String[] a){
System.out.println(Constants.PI_NUMBER);
System.out.println(Constants.DEFAULT_COUNTRY);
}
}
- 特殊的常量:接口内定义的变量默认是常量
public interface SpecialAnimal {
String color = "yellow"; //default: public static final
public void move();
}
public class Cat implements SpecialAnimal {
public void move() {
System.out.println("I can move");
}
public static void main(String[] args) {
Cat cat = new Cat();
cat.color = "white"; //error, the variables in interface are constants.
}
}
4.2 常量池
-
Java为很多基本类型的包装类/字符串建立了常量池
-
常量池:相同的值只存储一份,节省内存,共享访问
-
基本类型的包装类及其常量池:
- 用途:将基本数据类型转换为对象,以便在需要对象时使用,提供常量如
Integer.MAX_VALUE和Integer.MIN_VALUE等,提供方法如Integer.parseInt("123")等。 - 装箱:将基本数据类型转换为包装类对象。例如,
Integer aObj = Integer.valueOf(23);。
- 拆箱:将包装类对象转换为基本数据类型。例如,
int a = aObj.intValue();。
- 简介
- boolean:只有true和false两个值,包装类为Boolean
- byte:8位,范围是-128到127,包装类为Byte。
- short:16位,范围是-32768到32767,包装类为Short。
- int:32位,范围是-2^31到2^31-1,包装类为Integer。
- long:64位,范围是-2^63到2^63-1,包装类为Long。
- char:16位,范围是’\u0000’到’\uffff’,包装类为Character。
- float:32位,范围是1.4E-45到3.4028235E38,包装类为Float。
- double:64位,范围是4.9E-324到1.7976931348623157E308,包装类为Double。
- 其中
BooleanByteShortIntegerLongCharacter有常量池Boolean: true, falseByteShortIntLong: -128~127Character: 0~127
- 用途:将基本数据类型转换为对象,以便在需要对象时使用,提供常量如
public class IntegerTest {
public static void main(String[] args) {
Integer n1 = 127;
Integer n2 = 127;
System.out.println(n1==n2); // true
Integer n3 = 128;
Integer n4 = 128;
System.out.println(n3==n4); // false
Integer n5 = new Integer(127);
System.out.println(n1==n5); // false
}
}
public class CacheTest {
public static void main(String[] args) {
Boolean b1 = true; //true,false
Boolean b2 = true;
System.out.println("Boolean Test: " + String.valueOf(b1 == b2)); // true
Byte b3 = 127; //\u0000-\u007f
Byte b4 = 127;
System.out.println("Byte Test: " + String.valueOf(b3 == b4)); // true
Character c1 = 127; //\u0000-\u007f
Character c2 = 127;
System.out.println("Character Test: " + String.valueOf(c1 == c2));// true
Short s1 = -128; //-128~127
Short s2 = -128;
System.out.println("Short Test: " + String.valueOf(s1 == s2)); // true
Integer i1 = -128; //-128~127
Integer i2 = -128;
System.out.println("Integer Test: " + String.valueOf(i1 == i2)); // true
Long l1 = -128L; //-128~127
Long l2 = -128L;
System.out.println("Long Test: " + String.valueOf(l1 == l2)); // true
Float f1 = 0.5f;
Float f2 = 0.5f;
System.out.println("Float Test: " + String.valueOf(f1 == f2)); // false
Double d1 = 0.5;
Double d2 = 0.5;
System.out.println("Double Test: " + String.valueOf(d1 == d2)); // false
}
}
- 字符串的常量池
public class StringConstantTest {
public static void main(String[] args) {
String s1 = "abc";
String s2 = "abc";
String s3 = "ab" + "c";
String s4 = "a" + "b" + "c"; //都是常量,编译器将优化
System.out.println(s1 == s2); //true
System.out.println(s1 == s3); //true
System.out.println(s1 == s4); //true
}
}
-
基本类型的包装类和字符串有两种创建方式
- 常量式(字面量)复制创建,放在栈内存(将被常量化)
Integer a = 10;
String b = "abc"; new对象进行创建,放在堆内存(不会被常量化)
Integer c = new Integer(10)
String d = new String("abc")
- 常量式(字面量)复制创建,放在栈内存(将被常量化)
-
基本类型和包装类比较,将对包装类自动拆箱;加法+,将自动拆箱;对象比较,将比较地址
// 基本类型的包装类
public class BoxClassTest {
public static void main(String[] args){
int i1 = 10;
Integer i2 = 10; // 自动装箱
System.out.println(i1 == i2); //true
// 自动拆箱 基本类型和包装类进行比较,包装类自动拆箱
Integer i3 = new Integer(10);
System.out.println(i1 == i3); //true
// 自动拆箱 基本类型和包装类进行比较,包装类自动拆箱
System.out.println(i2 == i3); //false
// 两个对象比较,比较其地址
// i2是常量,放在栈内存常量池,i3是new出的对象,放在堆内存中
Integer i4 = new Integer(5);
Integer i5 = new Integer(5);
System.out.println(i1 == (i4+i5)); //true
System.out.println(i2 == (i4+i5)); //true
System.out.println(i3 == (i4+i5)); //true
// i4+i5 操作会使得i4, i5自动拆箱为基本类型并运算得到10
// 基础类型10和对象比较,会使对象自动拆箱,做基本类型比较
Integer i6 = i4 + i5; // +操作使i4, i5自动拆箱,得到10,因此i6 == i2
System.out.println(i1 == i6); //true
System.out.println(i2 == i6); //true
System.out.println(i3 == i6); //false
}
}
- 常量赋值(堆内存)和
new创建(栈内存)不是同一个对象;编译器只会优化确定的字符串并缓存
// 字符串
public class StringNewTest {
public static void main(String[] args) {
String s0 = "abcdef";
String s1 = "abc";
String s2 = "abc";
String s3 = new String("abc");
String s4 = new String("abc");
System.out.println(s1 == s2); //true 常量池
System.out.println(s1 == s3); //false 一个栈内存,一个堆内存
System.out.println(s3 == s4); //false 两个都是堆内存
System.out.println("=========================");
String s5 = s1 + "def"; //涉及到变量,编译器不优化
String s6 = "abc" + "def"; //都是常量,编译器自动优化成abcdef
String s7 = "abc" + new String ("def");//涉及到new对象,编译器不优化
System.out.println(s5 == s6); //false
System.out.println(s5 == s7); //false
System.out.println(s6 == s7); //false
System.out.println(s0 == s6); //true
System.out.println("=========================");
String s8 = s3 + "def";//涉及到new对象,编译器不优化
String s9 = s4 + "def";//涉及到new对象,编译器不优化
String s10 = s3 + new String("def");//涉及到new对象,编译器不优化
System.out.println(s8 == s9); //false
System.out.println(s8 == s10); //false
System.out.println(s9 == s10); //false
}
}
5 不可变对象和字符串
5.1 不可变对象
- 可变对象(Mutable Object):即普通对象(自建的对象)
- 不可变对象(Immutable Object):一旦创建,对象(状态/值)就不能被更改了,其内在的成员变量的值也不能修改了
- 包括八个基本类型的包装类, String, BigInteger, BigDecimal等
- 不可变对象的赋值:传指针
- 不可变对象的函数调用:传指针,临时变量指向新内存,外部实参的指针不改动
- 优点
- 只读,线程安全
- 并发读,提高性能
- 可以重复使用
- 缺点:制造垃圾,浪费空间
String a = new String("abc");
String b = a; // 指针b和a都指向"abc"
System.out.println(b); // abc
a = "def"; // 指针a指向"def",指针b仍指向"abc"
System.out.println(b); // abc
public static void change(String b){
// 对象调用函数,实参的指针传给形参的指针,指针a和指针b都指向"abc"
b = "def"; // 指针b指向"def",指针a不变
}
a = new String("abc"); // 指针a指向abc
change(a);
System.out.println(a); // abc
// 可变对象和不可变对象传递参数
public class ArgumentPassing {
public static void changeValue(int a){
// 函数是基本类型,传值
a = 10;
}
public static void changeValue(String s1){
// 传指针,b和s1都指向abc
s1 = "def"; // s1指向def,b不变
}
public static void changeValue(StringBuffer s1){
// 传指针,c和s1都指向abc
s1.append("def"); // 原地扩张,c和s1指向的abc变为abcdef
}
public static void main(String[] args) {
int a = 5; //基本类型
String b = "abc"; //不可变对象
StringBuffer c = new StringBuffer("abc"); //可变对象
changeValue(a);
changeValue(b);
changeValue(c);
System.out.println(a); // 10
System.out.println(b); // abc
System.out.println(c); // abcdef
}
}
- 创建不可变对象
- 所有属性都是
final和private的 - 不提供 setter 方法
- 类是
final的,或所有的方法都是final的 - immutable 对象不可改变,如果有改变,
clone/new一个对象进行修改 - 若类中包含 mutable 对象,返回拷贝需要深度
clone
- 所有属性都是
5.2 字符串
-
是一种不可变对象
-
定义
- 常量赋值,栈分配内存
String a = "abc" - new 对象,堆分配内存
String b = new String("abc")
- 常量赋值,栈分配内存
-
比较
- 字符串内容比较:
equals方法 - 是否指向同一个对象:指针比较
==
- 字符串内容比较:
-
加法
a = a + "def":效率差,内存占用多(原a和后来的a+"def"各自占用不同空间)- 使用
StringBuffer类的append方法进行修改:同步,线程安全,修改快速 - 使用
StringBuilder类的append方法进行修改:不同步,线程不安全,修改更快
import java.util.Calendar;
public class StringAppendTest {
public static void main(String[] args) {
int n = 50000;
Calendar t1 = Calendar.getInstance();
String a = new String();
for(int i=0;i<n;i++){
a = a + i + ",";
}
System.out.println(Calendar.getInstance().getTimeInMillis() - t1.getTimeInMillis()); // 输出所用时间ms数
Calendar t2 = Calendar.getInstance();
StringBuffer b = new StringBuffer("");
for(int i=0;i<n;i++){
b.append(i);
b.append(",");
}
System.out.println(Calendar.getInstance().getTimeInMillis() - t2.getTimeInMillis()); // 输出所用时间ms数
Calendar t3 = Calendar.getInstance();
StringBuilder c = new StringBuilder("");
for(int i=0;i<n;i++){
b.append(i);
b.append(",");
}
System.out.println(Calendar.getInstance().getTimeInMillis() - t3.getTimeInMillis()); // 输出所用时间ms数
}
}
public class ArgumentPassing {
public static void changeValue(int a){
// 函数是基本类型,传值
a = 10;
}
public static void changeValue(String s1){
// 传指针,b和s1都指向abc
s1 = "def"; // s1指向def,b不变
}
public static void changeValue(StringBuffer s1){
// 传指针,c和s1都指向abc
s1.append("def"); // 原地扩张,c和s1指向的abc变为abcdef
}
public static void main(String[] args) {
int a = 5; //基本类型
String b = "abc"; //不可变对象
StringBuffer c = new StringBuffer("abc"); //可变对象
changeValue(a);
changeValue(b);
changeValue(c);
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
}
第七章 package和import
1 package 和 import
1.1 package
- 在java类文件的第一句话给出包的名称
- 类全称 cn.edu.ecnu.PackageExample,短名称 PackageExample
- 引用类的时候必须采用全称引用,正文可以用短名称
// cn/edu/ecnu/PackageExample.java
package cn.edu.ecnu;
public class PackageExample{}
- 包名 package name 尽量唯一
1.2 import
- import 必须全部放在 package 之后,类定义之前
- 可以用*来引入一个目录下的所有类,比如import.java.lang.*,但不会引入该目录下的子目录文件,即不包括java.lang.reflect.*
//cn/edu/ecnu/PackageExample.java
package cn.edu.ecnu;
public class PackageExample{}
package cn.edu.ecnu;
import cn.edu.ecnu.PackageExample;
//也可以采用import cn.edu.ecnu.*
//如果PackageExample和当前类在同一个目录,可以省略上句import
public class PackageExampleTest {
public static void main(String[] args) {
PackageExample obj = new PackageExample();
//此处可以用类的短名称来引用
}
}
- 如果想引用不同包里的同名类,有两种办法
import a.*和import b.*,然后调用时使用a.Man和b.Manimport a.Man,调用时使用Man和b.Man(程序中需要引用多个同名的类时,只能import其中一个,并可用类名调用,其他的类必须用全称调用)
package c;
import a.*;
import b.*;
public class Test {
public static void main(String[] args){
a.Man m = new a.Man();
a.Man n = new b.Man();
}
}
package c;
import a.Man;
public class Test {
public static void main(String[] args){
Man m = new Man();
a.Man n = new b.Man();
}
}
2 jar文件导出和导入
- jar文件:扩展名.jar,用于可执行程序文件的传播,实际上是一组class文件的压缩包
- jar文件只包括class,没有java文件
3 package 和 import——命令行
X 表示任意盘符或路径
-
编译:
X:\>java c:\temp\cn\com\test\Man.java- 规则
- 需要 java 文件的全路径,包括扩展名
- 需要给出这个类所依赖的类(包括依赖的类再次依赖的所有其他类)的所在路径
- 规则
-
运行:
X:\>java -cp .;c:\temp cn.com.test.Man- 解释
java:执行命令, java.exe 的简写-cp:固定格式参数,-classpath的简写.;c:\temp:一个由分号(Windows)或冒号(Linux或Mac)连接起来的字符串。按分隔符隔开得到一堆子路径。如果在运行cn.com.test.Man的过程中用到了其他类,就会从本部分的子路径中依次寻找相应类cn.com.test.Man:主执行类的全称
- 规则:
- 需要类名全称(非文件路径),无需写扩展名
- 需要给出这个类以及被依赖类的路径综合
- classpath 参数可以包含 jar 包
- 如果路径内有空格,将 classpath 参数整体加双引号:
java -classpath ".;c:\test.jar;c:\temp;c:\a bc" cn.com.test.Man
- 解释
4 java访问权限
- 四种访问权限
private:只能本类访问defalut:同一个包内访问protected:同一个包,子类均可以访问public:所有类都可以访问- 都可以修饰成员变量、成员方法、构造函数,其中
default和public可以修饰类
package test1;
public class A {
private int v1 = 1;
int v2 = 2;
protected int v3 = 3;
public int v4 = 4;
private void showV1(){
System.out.println(v1);
}
void showV2(){
System.out.println(v2);
}
protected void showV3(){
System.out.println(v3);
}
public void showV4(){
System.out.println(v4);
}
}
package test1;
//B and A are in the same package
public class B {
public void show(){
//B is not subclass of A
// System.out.println(v1); //error
// System.out.println(v2); //error
// System.out.println(v3); //error
// System.out.println(v4); //error
// showV1(); //error
// showV2(); //error
// showV3(); //error
// showV4(); //error
A obj = new A();
//System.out.println(obj.v1); error, private
System.out.println(obj.v2);
System.out.println(obj.v3);
System.out.println(obj.v4);
//obj.showV1(); error, private
obj.showV2();
obj.showV3();
obj.showV4();
}
}
package test1;
//C is a subclass of A, and in the same package of A.
public class C extends A {
public void show(){
// C作为A的子类
//System.out.println(v1); error, private
System.out.println(v2);
System.out.println(v3);
System.out.println(v4);
//showV1(); error, private
showV2();
showV3();
showV4();
// C作为A的平级
A obj = new A();
//System.out.println(obj.v1); error, private
System.out.println(obj.v2);
System.out.println(obj.v3);
System.out.println(obj.v4);
//obj.showV1(); error, private
obj.showV2();
obj.showV3();
obj.showV4();
}
}
package test2;
import test1.A;
public class D extends A{
public void show(){
// D作为A的子类
//System.out.println(v1); error, private
//System.out.println(v2); error, default
System.out.println(v3);
System.out.println(v4);
//showV1(); error, private
//showV2(); error, default
showV3();
showV4();
// D作为A的平级
A obj = new A();
//System.out.println(obj.v1); error, private
//System.out.println(obj.v2); error, default
//System.out.println(obj.v3); error, protected ֻ只有作为子类才能访问
System.out.println(obj.v4);
//obj.showV1(); error, private
//obj.showV2(); error, default
//obj.showV3(); error protected 只有作为子类才能访问
obj.showV4();
}
}
package test2;
import test1.A;
public class E {
public void show(){
//E is not a subclass of A. And E is not in the same package of A.
//System.out.println(v1); error, private
//System.out.println(v2); error, default
//System.out.println(v3);
//System.out.println(v4);
//showV1(); error, private
//showV2(); error, default
//showV3();
//showV4();
A obj = new A();
//System.out.println(obj.v1); error, private
//System.out.println(obj.v2); error, default
//System.out.println(obj.v3); error, protected ֻ只有作为子类才能访问
System.out.println(obj.v4);
//obj.showV1(); error, private
//obj.showV2(); error, default
//obj.showV3(); error protected ֻ只有作为子类才能访问
obj.showV4();
}
}
