这两天学习了疯狂Java讲义的第五章和第六章的一部分:
一、引用变量的强制类型转换
类型转换运算符是小括号,用法如下:(type)variable。
进行强制类型转换时需要注意:
1.基本类型之间的转换只能在数值类型之间进行,这里所说的数值类型包括整数型、字符型和浮点型。但数值型不能和布尔型之间进行类型转换。2.引用类型之间的转换只能把一个父类变量转换成子类类型,如果是两个没有任何继承关系的类型,则无法进行类型转换。
在进行类型转换之前,先用instanceof运算符判断是否可以成功转换,从而避免出现异常。
二、instanceof运算符
Instanceof运算符的前一个操作数通常是一个引用类型的变量,后一个操作数通常是一个类(也可以是接口),它用于判断前面的对象是否是后面的类,或者其子类、实现类的实例。如果是,则返回true,否则返回false。
在使用instanceof运算符时需要注意:instanceof运算符前面操作数的编译时类型要么与后面的类相同,要么是后面类的父类,否则会引起编译错误。
三、利用组合实现复用
class Animal
{
private void beat()
{
System.out.println("心脏跳动...");
}
public void breath()
{
beat();
System.out.println("吸一口气,吐一口气,呼吸中...");
}
}
class Bird
{
private Animal a;
public Bird(Animal a)
{
this.a = a;
}
public void breath()
{
a.breath();
}
public void fly()
{
System.out.println("我在天空自在的飞翔...");
}
}
class Wolf
{
private Animal a;
public Wolf(Animal a)
{
this.a = a;
}
public void breath()
{
a.breath();
}
public void run()
{
System.out.println("我在陆地上快速奔跑...");
}
}
public class TestComposite
{
public static void main(String[] args)
{
Animal a1 = new Animal();
Bird b = new Bird(a1);
b.breath();
b.fly();
Animal a2 = new Animal();
Wolf w = new Wolf(a2);
w.breath();
w.run();
}
}
继承是对已有的类做一番改造,以此获得一个特殊的版本,就是将一个较为抽象的类改造成能适用于某些特定需求的类。如果两个类之间有明确的整体、部分关系,则应该采用组合关系实现复用。
四、单例类
如果一个类始终只能创建一个实例,则这个类被称为单例类。
为了避免其他类自由创建该类的实例,我们把该类的构造器使用private修饰,从而把该类的所有构造器隐藏起来。
根据良好封装的原则:一旦把该类的构造器隐藏起来,则需要提供一个public方法作为该类的访问点,用于创建该类的对象,且该方法必须使用static修饰(因为调用该方法之前还不存在对象,因此调用该方法的不可能是对象,只能是类)。
除此之外,该类还必须缓存已经创建的对象,否则该类无法知道是否曾经创建过对象,也就无法保证只创建一个对象。为此该类需要使用一个属性来保存曾经创建的对象,因为该属性需要被上面的静态方法访问,故该属性必须使用static修饰。
class Singleton
{
//使用一个变量来缓存曾经创建的实例
private static Singleton instance;
//将构造器使用private修饰,隐藏该构造器
private Singleton(){}
//提供一个静态方法,用于返回Singleton实例
//该方法可以加入自定义的控制,保证只产生一个Singleton对象
public static Singleton getInstance()
{
//如果instance为null,表明还不曾创建Singleton对象
//如果instance不为null,则表明已经创建了Singleton对象,将不会执行该方法
if(instance == null)
{
//创建一个Singleton对象,并将其缓存起来
instance = new Singleton();
}
return instance;
}
}
public class TestSingleton
{
public static void main(String[] args)
{
//创建Singleton对象不能通过构造器,只能通过getInstance方法
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
//将输出true
System.out.println(s1 == s2);
}
}
五、final修饰符
final关键字可用于修饰类、变量和方法,表示它修饰的类、方法和变量不可改变。
1.final修饰成员变量成员变量是随类初始化或对象初始化而初始化的。对于final修饰的成员变量而言,一旦有了初始值之后,就不能被重新赋值。所以成员变量只能在定义该成员变量时指定默认值,或者在静态初始化块、初始化块和构造器为成员变量指定初始值。
final成员变量必须显示初始化,系统不会对final成员进行隐式初始化。
2.final修饰局部变量
final修饰的局部变量可以在定义时指定默认值,也可在后面代码中对其赋值。
由final修饰的形参不能被赋值。
3.final修饰引用类型变量
使用final修饰的引用类型变量不能被重新赋值,但可以改变引用类型变量所引用对象的内容。
4.final方法final修饰方法不可被重写。
5.final类
final修饰的类不可有子类。
6.不可变类
不可变类的意思是创建该类的实例后,该实例的属性是不可变的。
创建自定义的不可变类,可遵循以下规则:
* 使用private和final修饰符来修饰该类的属性。 * 提供带参数构造器,用于根据传入参数来初始化类里的属性。* 仅为该类的属性提供getter方法,不要为该类的属性提供setter方法,因为普通方法无法修改final修饰的属性。
* 如果有必要,重写Object类中的hashCode和equals方法。在equals方法根据关键属性来作为两个对象相等的标准,除此之外,还应该保证两个用equals方法判断为相等的对象的hashCode也相等。
public class Address
{
private final String detail;
private final String postCode;
//在构造器里初始化两个实例属性
public Address()
{
this.detail = "";
this.postCode = "";
}
public Address(String detail , String postCode)
{
this.detail = detail;
this.postCode = postCode;
}
//仅为两个实例属性提供getter方法
public String getDetail()
{
return this.detail;
}
public String getPostCode()
{
return this.postCode;
}
//重写equals方法,判断两个对象是否相等
public boolean equals(Object obj)
{
if (obj instanceof Address)
{
Address ad = (Address)obj;
if(this.getDetail().equals(ad.getDetail())
&& this.getPostCode().equals(ad.getPostCode()))
{
return true;
}
}
return false;
}
public int hashCode()
{
return detail.hashCode() + postCode.hashCode();
}
}
六、抽象类
抽象方法是只有方法签名,没有方法实现的方法。抽象方法和抽象类必须使用abstract修饰符来定义,有抽象方法的类只能被定义成抽象类,抽象类里可以没有抽象方法。
抽象方法和抽象类的规则如下:
* 抽象类和抽象方法必须使用abstract修饰符来修饰。* 抽象方法不能有方法体,抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。
* 抽象类可以包含属性、方法(包括普通方法和抽象方法)、构造器、初始化块、内部类、枚举类六种成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用。
* 含有抽象方法的类(包括直接定义了一个抽象方法;继承了一个抽象父类,但没有完全实现父类包含的抽象方法;以及实现了一个接口,但没有完全实现接口包含的抽象方法三种情况)只能被定义成抽象类。
当abstract修饰类时,表明这个类只能被继承;当abstract修饰方法时,表明这个方法必须由子类提供实现(即重写)。
final和abstract不能同时使用,static和abstract不能同时修饰某个方法。
public abstract class Shape
{
{
System.out.println("执行Shape的初始化块...");
}
private String color;
//定义一个计算周长的抽象方法
public abstract double calPerimeter();
//定义一个返回形状的抽象方法
public abstract String getType();
//定义Shape的构造器,该构造器并不是用于创建Shape对象,而是用于被子类调用
public Shape(){}
public Shape(String color)
{
System.out.println("执行Shape的构造器...");
this.color = color;
}
public void setColor(String color)
{
this.color = color;
}
public String getColor()
{
return this.color;
}
}
public class Triangle extends Shape
{
//定义三角形的三边
private double a;
private double b;
private double c;
public Triangle(String color ,double a ,double b ,double c)
{
super(color);
this.setSides(a,b,c);
}
public void setSides(double a ,double b ,double c)
{
if(a>=b+c || b>=a+c || c>=a+b)
{
System.out.println("三角形两边之和必须大于第三边");
return;
}
this.a = a;
this.b = b;
this.c = c;
}
//重写Shape类的计算周长的抽象方法
public double calPerimeter()
{
return a+b+c;
}
//重写Shape类的返回形状的抽象方法
public String getType()
{
return "三角形";
}
public static void main(String[] args)
{
Shape s = new Triangle("黑色",3,4,5);
System.out.println(s.getType());
System.out.println(s.calPerimeter());
}
}