Java复用类
组合与继承
- 组合:在新的类中产生现有类的对象。
- 继承:按照现有类的类型来创建新类,无需改变现有类的形式,采用现有类的形式并在其中添加新的代码。
组合:
public class TestDemo1 {
public static void main(String[] args) {
A a = new A();
a.f1();
}
}
class A{
public void f1(){
B b = new B("哈登");//组合
b.f2();
System.out.println(b);// 如果不覆写B类中的toString(),那么就会输出b对象的地址
}
}
class B{
private int age;
private String name ;
public B(String name){
this.name = name;
this.age=28;
}
public void f2(){
System.out.println("姓名为:"+name+" ,年龄为:"+age);
}
@Override
public String toString() {
return name;
}
}
打印结果
这里在B类的构造方法中初始化了age的值,如果将this.age=28注释掉,那么年龄为0.
关于初始化
1.类中域为基本类型时,默认自动初始化为0
2.类中域为引用类型时,默认自动初始化为null
初始化位置
1.在定义对象的地方。意味着在调用构造器之前进行初始化
2.在类的构造器中
3.实例初始化
4.延迟初始化(惰性初始化)在使用对象之前
看一下具体代码
public class ChuShiHua {
public static void main(String[] args) {
Student student = new Student("Messi");
System.out.println(student);
}
}
class Student {
//1.在定义对象的地方。意味着在调用构造器之前进行初始化
private String name1="Ronaldo";
private String name2,name3,name4;
//2.在类的构造器中
public Student(String name2){
this.name2 = name2;
}
// 3.实例初始化
{
name3="Alice";
}
// 4.延迟初始化(惰性初始化)在使用对象之前
@Override
public String toString() {
if(name4==null){
name4="Peter";
}
return name1+" "+name2+" "+name3+ " "+ name4;
}
}
继承
class Fu{
public Fu(){
System.out.println("这是父类构造");
}
}
class Son extends Fu{
public Son(){
System.out.println("这是子类构造");
}
}
public class JiCheng {
public static void main(String[] args) {
new Son();
}
}
这里Son类继承了Fu类。关键字是extends.无论如何,父类构造总是先被初始化,然后再初始化子类构造。
子类默认调的是父类的无参构造,如果想要调用父类的有参构造,那么在子类构造的第一句话写上super(..)
class Fu{
int age;
public Fu(){
System.out.println("aaa");
}
public Fu(int age){
this.age = age;
System.out.println("这是父类构造"+this.age);
}
}
class Son extends Fu{
int i;
public Son(int i){
**super(20);**
this.i = i ;
System.out.println("这是子类构造"+ i);
}
}
public class JiCheng {
public static void main(String[] args) {
new Son(10);
}
}
protected关键字
- 对于类的用户而言,是私有的
- 对于其子类或者同一包的类而言,是可以访问的
在F类中定义了一个protected修饰的方法,在Z类中可以访问的。
向上转型
继承的最大用处并不是复用类,而是多态。多态又是基于继承的。
class Instrument{
static void play(Instrument e){
System.out.println("通过向上转型"+e);
}
}
public class Wind extends Instrument{
public static void main(String[] args) {
Wind wind = new Wind();
Instrument.play(wind);
}
}
在play()方法中,将Wind对象传入到父类Instrument的play()方法中,照样能够打印出Wind对象。用父类的引用去接收子类的对象,其指向的是子类对象。
final关键字
final所描述的对象是无法改变的。主要用在类,方法,数据中。
数据
告知编译器这块数据是无法改变的。
1.一个永不改变的编译时常量---减轻了运行时的负担
2.一个运行时被初始化的值,而你并不需要它被改变
3.一个即是static 又是 final 的数据描述的是只占据一块不能改变的存储空间
4.当final作用在基本类型的时候,指的是数值无法改变。当fianl作用于对象的引用时,指的是对象的引用不变,但对象本身还是可以改变的。
class FinalData{
int num = 1;
public final int age1 =20;
/*
* 1.用static final 修饰的变量应该字母全部大写
* 2.用public可以无限制被访问
* 3.static:强调只有一份
* 4.final: 说明它是一个常量
*
* */
public static final int AGE = 10;
}
public class FinalTest {
public static void main(String[] args) {
FinalData fd = new FinalData();
System.out.println(fd.age1);//输出20
// fd.age1=30; 由于age是final所修饰的基本类型,数值不能被改变。
final FinalData fdFinal = new FinalData();
fdFinal.num=30;
System.out.println(fdFinal.num);//打印30
//无法编译,因为fdFinal的引用是不能被改变的
// fdFinal = new FinalData();
}
}
final参数
class FinalData{
public void fun(final int i){
// i++;无法编译,因为i是final类型,值无法改变
}
public void fun2(final FinalData f){
f.fun(1);
// f = new FinalData(); 无法编译,因为f的引用不可变
}
}
public class FinalTest {
public static void main(String[] args) {
}
}
final方法
作用:
- 把方法锁定,防止任何继承类修改它的含义
- 效率
类中所以的private方法都是final类型的,由于无法取用private方法,所以也就无法覆盖它。
class GrandFather{
private final void fun(){
System.out.println("11");
}
}
class Father extends GrandFather{
private final void fun(){
System.out.println("22");
}
}
class Son extends Father{
public final void fun(){
System.out.println("33");
}
}
public class FinalMethodTest {
public static void main(String[] args) {
Son s = new Son();
s.fun();//打印33
GrandFather f = s;
//f.fun(); 无法编译
}
}
类内所有 private 方法都自动成为 final。由于我们不能访问一个 private 方法,所以它绝对不会被其他方
法覆盖(若强行这样做,编译器会给出错误提示)。可为一个 private 方法添加 final 指示符,但却不能为
那个方法提供任何额外的含义。
final类
final类描述的是该类没有子类,无法被继承。
可以看到上面代码报错,无法编译,原因是父类被声明为final类型。
在一个类中如果被声明为final类型,那么该类的方法隐式的都被声明为final类型。
初始化及类的加载
class Fu {
private int age =70;
private String name;
public static String address = "崇明岛";
static {
System.out.println("这是父类的static静态块");
}
public Fu(){
System.out.println("这是父类构造");
}
public void fu(){
System.out.println(age+name+address);
}
}
class Zi extends Fu{
private int age=30 ;
private String name;
public static String address = "海南岛";
static {
System.out.println("这是子类的static静态块");
}
public Zi(){
System.out.println("这是子类构造");
}
public void zi(){
System.out.println(age+name+address);
}
}
public class CSHTest extends Zi{
private int age=10 ;
private String name;
public static String address = "台湾岛";
static {
System.out.println("这是孙类的static静态块");
}
public CSHTest(){
System.out.println("这是孙类构造");
}
public void test(){
System.out.println(age+name+address);
}
public static void main(String[] args) {
CSHTest t = new CSHTest();
t.test();
}
}
首先执行CSHTEST类的main方法,类加载器查找CSHTEST编译后的代码.class,发现其有一个父类Zi,于是加载Zi,发现它又有一个父类,于是再加载Fu,此时,根基类中的static初始化(static修饰符描述的方法只执行一次),然后是下一个导出类,以此类推。此种方式很重要。因为导出类初始化有可能依赖于父类的初始化。
此时类已经加载完毕了。现在可以创建对象了。首先,对象中的基本类型都被初始化为0,对象的引用都被初始化为null,然后,基类的构造器就被调用,在本例中,是默认调用的,默认调用super(),如果想指定调用有参构造,那么自己写super(Type Args).
总结就是:加载父类的静态块--->加载子类静态块---->加载父类的构造--->加载子类构造
声明
所写内容是《Thinking In Java》读书笔记