1、组合
由于新的类是由现有对象所组成,这种现象叫组合;
编译器并不是简单地为每一个引用都创建一个默认对象,因此,若像初始化这些引用,可以在代码中的下列位置进行:
- 在定义对象的地方,这意味着他们总是能够在构造器被调用之前初始化;
- 在类的构造器中;
- 就在正要使用这些对象之前,即当需要一个实例的时候初始化对象,这种方式成为惰性初始化,在生成对象的情况下,这种方式可以减少额外的负担;
- 使用实例初始化;
惰性初始化:
class First{ First(){ System.out.print("First()"); } } public class Lazy{ First f; public void print(){ if(f==null) f = new First(); } public static void main(String[] args){ Lazy z = new Lazy(); z.print(); } }
2、继承
//:nuc/test1/Vector.java
package nuc.test;
class Vector3 {
public Vector3() {
System.out.println("我是Vector3");
}
}
class Vector2 extends Vector3 {
public Vector2() {
System.out.println("我是Vector2");
}
}
class Vector extends Vector2 {
public static void main(String[] args) {
Vector v = new Vector();
}
}
/* OutPut
我是Vector3
我是Vector2
*/
证明 :
- 构建过程是从基类向外扩散的;
- 基类构造器总是会被调用,并且在导出类构造器之前被调用;
- 即便不为vector创建构造器,编译器也会合成一个默认的构造器;
package nuc.test;
class Vector3 {
public Vector3(int i) {
System.out.println("我是Vector"+i);
}
}
class Vector2 extends Vector3 {
public Vector2(int i) {
super(i); //不调用会出错
System.out.println("我是Vector2");
}
}
public class Vector extends Vector2 {
public Vector() {
super(3);
System.out.println("我是Vector");
}
public static void main(String[] args) {
Vector v = new Vector();
}
}
证明:
- 如果不在Vector2中调用基类构造器,编译器将无法找到符合Vector3的构造器;
- 调用构造器必须是你在导出类构造器中做的第一件事;
3、代理
3.1 为什么要用代理模式
是Java的一种设计模式,有些对象由于跨网络或者安全等方面的原因,不能够直接或者不需要直接被访问,直接访问的代价会给系统带来不必要的复杂性,比如运行效率低下、访问延迟等原因,代理模式在一定程度上缓解了这种问题,相当于做真实对象的替身;
3.2 定义
为某对象提供一种代理以控制对该对象的访问,即客户端通过代理间接地访问对象,从而限制、增强或修改该对象的一些特性。
3.3 优点
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
3.4 缺点
- 在客户端和目标对象之间增加一个代理对象,会造成请求速度变慢;
- 增加系统的复杂度;
暂时理解到这里,日后读完Java设计模式继续写博客。
3.5简单代理模式:
//:nuc/test/test1.java
package nuc.test;
public class test1 {
private String s = "Housy";
public void up(String a) {
s += a;
}
public void down() {
up("down() ");
}
public void left() {
up("left() ");
}
public void right() {
up("right() ");
}
public String toString() {
return s;
}
}
//:nuc/test/Angent.java
package nuc.test;
import static nuc.test.Print.*;
public class Angent {
private test1 t = new test1();
public void up(String a) {
t.up(a);
}
public void down() {
t.down();
}
public void left() {
t.left();
}
public void right() {
t.right();
}
public static void main(String[] args) {
Angent angent = new Angent();
angent.down();
angent.left();
angent.right();
print(angent.t);
}
}
/* OutPut:Housydown() left() right() */
4、组合与继承的结合使用--->优雅的代码
package nuc.test;
import static nuc.test.Print.*;
class Plate {
Plate(int i) {
print("Plate constructor");
}
}
class DinnerPlate extends Plate {
DinnerPlate(int i) {
// TODO Auto-generated constructor stub
super(i); //必须先调用父类构造器
print("DinnerPlate constructor");
}
}
//a cultural way of doing something
class Custom {
public Custom(int i) {
// TODO Auto-generated constructor stub
print("Custom constructor");
}
}
public class test1 extends Custom {
private DinnerPlate dp;
public test1(int i) {
super(i + 1);
dp = new DinnerPlate(i + 2);
print("test1 constructor");
}
public static void main(String[] args) {
test1 t = new test1(8);
}
}
/*
* OutPut:
Custom constructor
Plate constructor
DinnerPlate constructor
test1 constructor
*/
- 最好编写自己的清理方法,但不要使用finalize();
- @Override可以防止不想重载时 意外重载;
5、组合与继承
组合是显示地在新的类中放置子对象,继承则是隐式地允许在新的类中放置子对象,组合技术通常用于想在新的类中使用现有类的功能而非接口;
6、protected关键字
就类用户而言是private 的,但对于任何继承此类的导出类或者其他任何位于同一个包内的类来说,他是可以访问的;
package main.test;
public class Test {
protected void test(){
System.out.println("protected test()");
}
}
package main;
import main.test.*;
public class Main extends Test {
void Maintest(){
test();
}
public static void main (String args[]){
Main m = new Main();
Test t = new Test();
m.Maintest();
// t.test(); 编译不能通过。
}
}/*Output:
protected test()
*/
7、向上转型
7.1 概念
先上代码:
package nuc.housy;
import static nuc.test.Print.*;
class test {
public void play() {
}
static void tune(test i) {
i.play();
}
}
public class test1 extends test {
public static void main(String[] args) {
test1 t = new test1();
test.tune(t);
}
}
将test1的引用转换为test的引用的动作称之为向上转型;
8、final关键字
- 一个既是final又是static的域只占据一段不能改变的存储空间;
- 既是final又是static的域(即编译期常量)将用大写表示,并且使用下划线分割各个单词;
- 可用作编译期常量;
- static强调只有一份,final说明他是一个常量;
8.1 final数据
- 对于基本类型,final使数值恒定不变;而对用对象引用,final使引用恒定不变。
- final修饰的基本类型,一旦被初始化后,不能再被赋值。
- final修饰的对象引用,一旦引用被初始化指向一个对象,就无法再把它改为指向另外一个对象。
8.1.1 final参数
使用final修饰方法的参数,若参数为基本类型,该参数不能在方法中修改其值。若参数为对象应用,该参数在方法中不 能修改其指向引用。
8.1.2 空final
所谓的”空白final”是指被声明的为final但又为给定初值的对象引用或者基本数据。无论在什么情况下,编译器都会去确保 final在使用前必须被初始化。若不进行初始化,会提示错误
8.2 final类
- 当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。
- final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。
8.3 final方法
“使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。
注:类的private方法会隐式地被指定为final方法。
8.4 final和static
class test {
private static Random random = new Random(47);
public final int v_1 = random.nextInt(20);
public final static int v_2 = random.nextInt(20);
}
public class Angent {
public static void main(String[] args) {
test fd_1 = new test();
test fd_2 = new test();
System.out.println("v_1 = " + fd_1.v_1 + " | v_2 = " + test.v_2);
System.out.println("v_1 = " + fd_2.v_1 + " | v_2 = " + test.v_2);
}
}
/* OutPut:
v_1 = 15 | v_2 = 18
v_1 = 13 | v_2 = 18
*/
从上述代码可以看出,final将数值定义为静态和非静态的区别。在fd_1和fd_2中,v_1的值都是唯一的,而v_2的值是一致的,并没有因为对象的创建而加以改变,因为其被static修饰,意味着在装载时已被初始化,而不是每次创建新对象时都初始化。
8.5 final和对象引用
我们已经了解到,如果使用final修饰了引用对象,引用对象被初始化后,不能再被指向另外一个对象,但是其内部的内容是否可以修改?
final Person person = new Person("li", 20);
person.setName("wang");
person.setAge(40);
System.out.println(person.toString());
// Log打印
Person{name='wang', age=40}
从上述代码,person对象被final修饰,同时初始化为name = “li”,age=”20”。然后调用其相应setXX()方法修改其值。从Log打印看出,person对象自身的成员值为修改后的值。意味着被final修饰的对象引用,只是对象的应用不能修改,但是其自身却是可以修改的。
9、初始化及类的加载
程序是作为启动的一部分立刻被加载的,然后是初始化,紧接着开始运行,可以说,类的代码在初次使用时才加载,但是当访问static域或者static方法时,也会发生加载。
参考文献:
https://blog.youkuaiyun.com/io_field/article/details/52830587