Java编程思想(2)

第6章 访问权限控制

1 访问权限控制的等级,从最大权限到最小权限依次为:public,protected,包访问权限(没有关键字)和private

2 如果想要使用类,可以在import语句中导入该类或使用该类的全名来指定

// 使用ArrayList的一种方法是使用其全名java.util.ArrayList来指定
public static void main(String[] args){
	java.util.ArrayList list = new java.util.ArrayList();
}

// 可以使用import导入这个类,而不需要写全名
import java.util.ArrayList
public static void main(String[] args){
    ArrayList list = new ArrayList();
}

3 如果使用package语句,它必须是文件中除注释外的第一句程序代码,在文件起始处写 package  xxxx ; 表示你声明该编译单元中public类名称是xxxx类库的一部分

4 Java解释器的运行过程:首先,找出环境变量CLASSPATH包含一个或多个目录,用作查看.class文件的根目录。从根目录开始,解释器获取包的名称并将每个句点替换成反斜杆,以从CLASSPATH根中产生一个路径名称(即,package foo.bar.baz变成foo/bar/baz)。得到的路径会与CLASSPATH中的各个不同的项相连接,解释器就在这些目录中查找与你所要创建的类名称相关的.class文件。

5 从java2开始,包名都是小写,如package com.baidu.tt

6 除非要使用包里的所有类,可以使用星号导入全部。最好还是单个导入避免冲突。

7 如果不提供任何访问权限修饰词,即没有public,protected和private , 则意味着它是“包访问权限”。

8 关键字protected处理的是继承的概念。子类可以访问父类的protected成员变量或成员函数

9 代码风格:将public成员前面,后面跟protected,包访问权限和private成员

10 类的访问权限限制:

  • 每个编译单元(文件)都只能有一个public类
  • public的类名必须完全与该编译单元的文件名一样,包括大小写
  • 编译单元内完全不带public类也是可以的,这种不太常见

11 类既不可以是private,也不可以是protected,只能是包访问权限或public

扫描二维码关注公众号,回复: 4261768 查看本文章

第7章 复用类

1 每个非基本类型的对象都有一个toString()方法。而且当编译器需要一个String而你只有一个对象时,该方法就会被调用。

2 当创建一个类时,就是在继承,如果未明确指出要从其他类中继承,就是在隐式地从Java的标准根类Object进行继承。Object是所有类的父类。

3 继承是用extends关键字,语法为 class A extends B { }

 4 可以为每个类都创建一个main()方法,可使每个类的单元测试变得简单易行。

class Soap{
	private String s = "Cleanser";
	public void append(String a){ s += a;}
	public void dilute(){ append(" dilute()");}
	public void apply(){ append(" apply()");}
	public void scrub(){ append(" scrub()"); }
	public String toString(){
		return s;
	}
	public static void main(String[] args){   // main()函数
		Cleanser x = new Cleanser();
		x.dilute();x.apply();x.scrub();
		System.out.println(x);
	}
}
public class Chocolate extends Soap {
	
	public void scrub(){
		append(" Chocolate.scrub()");
		super.scrub();
	}
	public void foam(){ append(" foam()");}
	public static void main(String[] args){    // main()函数
		Chocolate c = new Chocolate();
		c.dilute();
		c.apply();
		c.scrub();
		c.foam();
		System.out.println(c);
		System.out.println("Testing base class: ");
		Cleanser.main(args);
	}
}

5  Java用super关键字表示父类,super.func()表示调用父类的函数func()

6  Java会自动在子类的构造器中先调用父类构造器,找不到会报错。默认的构造器都不会带参数, 子类会自动调用父类的默认构造器,如果父类没有默认构造器,则需要用super显式地调用父类的构造器

class Art2{
	Art2(int i){
		System.out.println("Art constructor "+i);
	}
}
class Drawing2 extends Art2{
	Drawing2(int i){
		super(i);     // 调用构造器Art2(int i)
		System.out.println("Drawing constructor "+i);
	}
}
public class Chocolate extends Drawing2 {
	
	Chocolate(){
		super(11);   // 调用构造器Drawing2(int i)
		System.out.println("Chocolate constructor");
	}
	public static void main(String[] args){
		Chocolate c = new Chocolate();
	}
}

7 Java中没有析构函数

8 子类对父类的函数的重载,Java SE5 新增了@Override注解,添加了这个注解的函数只能被覆写,如果不小心重载这个函数,编译器会报错

class Homer2{
	char doh(char c){
		System.out.println("doh(char)");
		return 'd';
	}
	float doh(float f){
		System.out.println("doh(float)");
		return 1.0f;
	}	
}
class Milhouse{}

class Bart2 extends Homer2{
	void doh(Milhouse m){      // 对父类的函数重载
		System.out.println("doh(Milhouse)");
	}
}
public class Chocolate{
	
	public static void main(String[] args){
		Bart2 b = new Bart2();
		b.doh(1);
		b.doh('x');
		b.doh(1.0f);
		b.doh(new Milhouse());
	}
}

9 “is - a”的关系是用继承来表达,“has - a”的关系是用组合来表达。

10 向上转型,tune( Intrument i )函数将Instrument的子类Chocolate引用转换为Instrument引用,称之为向上引用。

class Instrument{
	public void play(){}
	static void tune(Instrument i){
		i.play();
	}
}
public class Chocolate extends Instrument{
	
	public static void main(String[] args){
		Chocolate c = new Chocolate();
		Instrument.tune(c);
	}
}

11 在Java中,这类常量必须是基本数据类型,并且以关键字final表示,定义时必须进行赋值。

12 一个既是static又是final的变量只占据一段不能改变的存储空间,既是static又是final的变量一般用大写表示。

        public static int getInt(){
		return 12;
	}
	private Random r = new Random();
	private final static int ONE = 1;
	private final static int TWO = getInt();   // 可以动态赋值,但必须是static
	private final int T = 12;
	private final int K = r.nextInt(12);

13 Java允许生成“空白final”,即被声明为final但又未给定初值的成员变量,可以在使用前赋值

class Poppet{
	private int i;
	Poppet(int i){
		this.i = i;
	}
}

public class Chocolate{
	private final int i = 0; // Initialized final
    // private final int k;   // error
	private final int j; // Blank final
	private final Poppet p; // Blank final
	public Chocolate(){
		j = 1;
		p = new Poppet(1);
	}
	public Chocolate(int x){
		j = x;
		p = new Poppet(x);
	}
	
	public static void main(String[] args){
		new Chocolate();
		new Chocolate(4);
	}
}

注:类的final成员变量不会被赋默认值,必须要在域的定义处所有构造器中用表达式对final成员变量进行赋值。Java确保final在使用前必须被初始化。

14 Java允许在参数列表中以声明的方式将参数声明为final,表示在函数中无法修改参数引用所指向的对象

class Gizmo{}

public class Chocolate{
	void with(final Gizmo g){
		g = new Gizmo();    // error,g为final参数,不能改变
	}
	public static void main(String[] args){
		
	}
}

15 使用final函数的原因有2个。第一个是把方法锁定,以防任何继承类修改该函数,不会被覆盖;第二个原因是效率,编译器对final函数的调用都转为内嵌调用,但只对代码小的函数内嵌调用会比较有用。

16 因为类的private函数,子类是没有权限调用,所以private函数默认都是final

class  WithFinals{
	private final void f(){System.out.println("WithFinals.f()");}
	private void g(){System.out.println("WithFinals.g()");}
}

class OverridingPrivate extends WithFinals{
	private final void f(){ System.out.println("OverringPrivate.f()"); }
	private void g(){ System.out.println("OverridingPrivate.g()");}
}

class OverridingPrivate2 extends OverridingPrivate{
	public final void f(){ System.out.println("OverringPrivate.f()"); }
	public void g(){ System.out.println("OverridingPrivate.g()");}
}

public class Chocolate{

	public static void main(String[] args){
		OverridingPrivate2 o = new OverridingPrivate2();
		o.f();
		o.g();
		OverridingPrivate o2 = o;
		o2.f();    // error
		o2.g();    // error
		WithFinals wf = o;
		wf.f();    // error
		wf.g();    // error
		
	}
}

17 当将某个类定义为final时,表示该类不能被继承。final class A {  . . .  } 。 由于final类不能被继承,所以该类中的所有方法都隐式指定为final,无法覆盖。

18 Java中所有事物都是对象。

第8章 多态

1 动态绑定,在运行时才会判断对象的类型。Java中除了static函数和final函数外,其他所有函数都是后期绑定

class A2{
	void play(){ System.out.println("A");}
}
class B extends A2{
	void play(){ System.out.println("B");}
}
class C extends A2{
	void play(){ System.out.println("C");}
}

public class Chocolate{

	public static void tune(A2 a){
		a.play();
	}
	public static void main(String[] args){
		A2 a = new B();
		a.play();    // 动态绑定,调用B的play()
		tune(a);     // 动态绑定,传入B类引用
	}
}

2 private函数无法被覆盖

public class Chocolate{
	private void f(){ System.out.println("private f()"); }
	public static void main(String[] args){
		Chocolate a = new B();
		a.f();    // 调用Chocolate的f()函数,因为是private类型,无法被覆盖
	}
}

class B extends Chocolate{
	public void f(){ System.out.println("public f()"); }
}

3 成员变量和静态方法都不具有多态性。当Sub对象转型为Super引用时,任何成员变量的访问操作都将由编译器解析,因此不会多态。Super.field和Sub.field分配了不同的存储空间,所以Sub实际由两个field变量。静态函数则是跟类相关。

class Super{
	public int field = 0;
	public int getField(){ return field; }
}
class Sub extends Super{
	public int field = 1;
	public int getField(){ return field;}
	public int getSuperField(){ return super.field;}
}
public class Chocolate{
	private void f(){ System.out.println("private f()"); }
	public static void main(String[] args){
		Super sup = new Sub();
		System.out.println("sup.field = "+sup.field + " , sup.getField() = "+sup.getField());
		Sub sub = new Sub();
		System.out.println("sub.field = "+sub.field + " , sub.getField() = "+sub.getField()+" , sub.getSuperField() = "+sub.getSuperField());

	}
}

输出
sup.field = 0 , sup.getField() = 1
sub.field = 1 , sub.getField() = 1 , sub.getSuperField() = 0
class StaticSuper{
	public static String staticGet(){
		return "Base staticGet()";
	}
	public String dynamicGet(){
		return "Derived dynamicGet()";
	}
}
class StaticSub extends StaticSuper{
	public static String staticGet(){
		return "Derived staticGet()";
	}
	public String dynamicGet(){
		return "Derived dynamicGet()";
	}
}
public class Chocolate{
	public static void main(String[] args){
		StaticSuper sup = new StaticSub();
		System.out.println(sup.staticGet());
		System.out.println(sup.dynamicGet());
	}
}

输出
Base staticGet()    // 因为是静态函数,没有动态调用子类的函数
Derived dynamicGet()   

4 构造器其实是隐式的static函数。

5 所有造器的调用顺序

  1. 调用基类构造器
  2. 按声明顺序初始化成员变量
  3. 调用自己的构造器主体
class Meal {
	private Bread b = new Bread();
	Meal(){System.out.println("Meal");}
}
class Bread {
	Bread(){System.out.println("Bread");}
}
class Cheese {
	Cheese(){System.out.println("Cheese");}
}
class Lettuce {
	Lettuce(){System.out.println("Lettuce");}
}
class Lunch extends Meal{
	private Lettuce l = new Lettuce();
	Lunch(){System.out.println("Lunch");}
}
class ProtableLunch extends Lunch{
	ProtableLunch(){System.out.println("ProtableLunch");}
}
public class Chocolate extends ProtableLunch{
	private Bread b = new Bread();
	private Cheese c = new Cheese();
	private Lettuce l = new Lettuce();
	private Chocolate(){ System.out.println("Chocolate");}
	public static void main(String[] args){
		new Chocolate();
	}
}


输出
Bread
Meal
Lettuce
Lunch
ProtableLunch
Bread
Cheese
Lettuce
Chocolate

6 记住销毁的顺序应该要跟初始化顺序相反。

7 构造器内部有多态函数的问题。如下所示Glyph的draw()会被RoundGlyph的draw()覆盖。初始化的实际过程如下

  1. 在其他任何事物发生之前,将分配给对象的存储空间初始化为二进制的零
  2. 调用基类的构造器,调用被覆盖后的draw()函数,但radius值为0
  3. 按照声明的顺序初始化成员变量
  4. 调用自身的构造器
class Glyph{
	void draw(){ System.out.println("Glyph.draw()");}
	Glyph(){
		System.out.println("Glyph() before draw()");
		draw();
		System.out.println("Glyph() after draw()");
	}
}
class RoundGlyph extends Glyph{
	private int radius = 1;
	RoundGlyph(int r){
		radius = r;
		System.out.println("RoundGlyph.RoundGlyph(), radius = "+radius);
	}
	void draw(){
		System.out.println("RoundGlyph.draw(), radius = "+radius);
	}
}
public class Chocolate{
	
	public static void main(String[] args){
		new RoundGlyph(4);
	}
}

输出
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 4

注:编写构造器有一条有效的准则:用尽可能简单的方法使对象进入正常状态,尽量避免调用其他函数

8 Java SE5添加了协变返回类型,表示子类的被覆盖函数可以返回父类函数返回类型的某种子类类型。

class Grain{
	public String toString(){ return "Grain"; }
}
class Wheat extends Grain{
	public String toString(){ return "Wheat"; }
}
class Mill{
	Grain process(){ return new Grain(); }
}
class WheatMill extends Mill{
	Wheat process(){ return new Wheat();}
}
public class Chocolate{
	
	public static void main(String[] args){
		Mill m = new Mill();
		Grain g = m.process();
		System.out.println(g);
		m = new WheatMill();
		g = m.process();
		System.out.println(g);
	}
}

输出
Grain
Wheat

9  向上转型会丢失具体的类型信息。可以采用向下转型

class Useful{
	public void f(){}
	public void g(){}
}
class MoreUseful extends Useful{
	public void f(){}
	public void g(){}
	public void u(){}
	public void v(){}
	public void w(){}
}

public class Chocolate{
	
	public static void main(String[] args){
		Useful[] x = {
				new Useful(),
				new MoreUseful()
		};
		x[0].f();   // ok
		x[1].g();   // ok
		x[1].u();    // error 因为x[1]向上转型,丢失了具体类型信息,无法调用u()函数
		((MoreUseful)x[0]).u();  // error,x[0]是Useful类型,不能向下转型为MoreUseful
		((MoreUseful)x[1]).u(); // ok,x[1]向下转型为MoreUseful
		
	}
}

第9章 接口

1 只有声明但没有函数体的函数称为抽象函数。如 abstract void f( ) 。 有抽象函数的类即为抽象类,抽象类不能创建对象,会报错。

2 如果一个类继承抽象类,并想创建该类对象,那么该子类必须要为抽象类的所有抽象函数提供函数定义。如果没有全部提供函数定义,该类还是抽象类。

3 抽象类并不要求所有的函数都是抽象函数

abstract class Instrument {   // 抽象类
	private int i;
	public abstract void play(int n);
	public String what(){ return "Instrument";}
	public abstract void adjust();
}
class Wind extends Instrument{
	public void play(int n){
		System.out.println("ok");
	}
	public String what(){ return "Instrument";}
	public abstract void adjust(){};
}

4 interface关键字定义一个完全抽象的类,即接口。所有函数只有声明没有函数体。接口被用来建立类与类之间的协议。

5 可以在interface关键字前面加public,仅限于该接口与其同名的文件中被定义。接口也可以包含成员变量,这些成员变量自动是static和final的。成员函数也自动为public。

6 实现接口是用implements。必须要实现所有函数的函数体

interface Instrument{
	int VALUE = 5;  // static & final
	void play(int n);  // automatically public
	void adjust();  // automatically public
}
class Wind implements Instrument{  // 实现接口必须要定义所有函数的函数体
	public void play(int n){ System.out.println("ok");}
	public void adjust(){}
}

7 只能继承一个类,但可以实现多个接口。需要将所有的接口名都置于implements关键字之后,用逗号将它们一一隔开。

interface CanFight{
	void fight();
}
interface CanSwim{
	void swim();
}
interface CanFly{
	void fly();
}
class ActionCharacter{
	public void fight(){}
}
class Hero extends ActionCharacter implements CanFight,CanSwim,CanFly{
	//public void fight(){};
	public void swim(){};
	public void fly(){};
}
public class ArrayApp {
	public static void t(CanFight x){ x.fight(); }
	public static void u(CanSwim x){ x.swim(); }
	public static void v(CanFly x){ x.fly(); }
	public static void w(ActionCharacter x){ x.fight(); }
	public static void main(String[] args){
		Hero h = new Hero();
		t(h);
		u(h);
		v(h);
		w(h);
	}
}

Hero类继承了ActionCharacter和实现接口CanFight,CanSwim和CanFly。Hero必须要实现所有接口的函数,但接口CanFight的fight()跟ActionCharacter的fight()一样,所以Hero无须再实现CanFight的fight()函数。t(),u(),v(),w()函数将父类和接口作为参数,所以Hero对象可以向上转型。

8 使用接口的核心原因:为了能够向上转型为多个基类型。第二个原因是防止客户端程序员创建该类的对象,并确保这仅仅是建立一个接口。

9 通过继承来扩展接口。类只能继承一个类,但接口可以继承多个接口


interface Monster{
	void menace();
}
interface DangerousMonster extends Monster{
	void destroy();
}
interface Lethal{
	void kill();
}

interface Vampire extends DangerousMonster,Lethal{  // 接口可以继承多个接口
	void drinkBlood();
}

10 在接口中定义的成员变量不能是空final,但可以被非常量表达式初始化。

interface Monster{
	Random RAND = new Random(47);
	int RANDOM_INT = RAND.nextInt(12);
	long RANDOM_LONG = RAND.nextLong()*10;
	float D = 4.32f;
}

11 接口是实现多重继承的途径,而生成遵循某个接口的对象的典型方式是工厂方法设计模式

interface Service{
	void method1();
	void method2();
}
interface ServiceFactory{
	Service getService();
}
class Implementation1 implements Service{
	Implementation1(){}
	public void method1(){ System.out.println("Implementation1 method1");}
	public void method2(){ System.out.println("Implementation1 method2");}	
}
class Implementation1Factory implements ServiceFactory{
	public Service getService(){
		return new Implementation1();
	}
}
class Implementation2 implements Service{
	Implementation2(){}
	public void method1(){ System.out.println("Implementation2 method1");}
	public void method2(){ System.out.println("Implementation2 method2");}	
}
class Implementation2Factory implements ServiceFactory{
	public Service getService(){
		return new Implementation2();
	}
}

public class ArrayApp {
	public static void serviceConsumer(ServiceFactory fact){
		Service s = fact.getService();
		s.method1();
		s.method2();
	}
	public static void main(String[] args){
		serviceConsumer(new Implementation1Factory());
		serviceConsumer(new Implementation2Factory());

	}
}

猜你喜欢

转载自blog.csdn.net/haima95/article/details/82732912