Java学习(四)


  1. Java允许使用包将类组织起来。使用包的主要原因是确保类名的唯一性。
    (1)类的导入
    一个类可以使用所属包中的所有类,以及其他包中的共有类。可以采用两种方式访问另一个包中的公有类。
    在每个类之前添加完整的包名:java.time.LocalDate tody = java.time.LocalDate.now();,这显然很繁琐。
    更简单且常用的方式是使用import语句。import语句时一种引用包含在包中的类的简明描述。一旦使用了import语句,在使用类时,就不必写出包的全名了。例:import java.util.*,也可以导入一个包中的特定类:import java.time.LocalDate
    在大多数情况下,只导入所需的包,并不比过多的理睬他们。但是发生命名冲突的时候,就不能不注意包的名字了。例如:java.utiljava.sql包都有日期(Date)类。如果在程序中同事导入了两个包:

    import java.util.*;
    import java.sql.*;
    

    在程序使用Date类的时候,就会出现一个编译错误:Date tody;,此时编译器无法确定程序使用的是哪一个Date类。可以增加一个特定的import语句来解决这个问题:

    import java.util.*;
    import java.sql.*;
    import java.util.Date;
    

    如果这两个Date类都需要使用,可以在每个类名前面加上完整的包名:

    java.util.Date deadline = new java.util.Date();
    java.sql.Date tody = new java.sql.Date(...);
    

    在包中定位类时编译器的工作。类文件中的字节码肯定使用完整的包名来引用其他类。
    (2)静态导入
    import语句不仅可以导入类,还增加了导入静态方法和静态域的功能
    例如,如果在源文件的顶部,添加一条指令:import static java.lang.System.*
    就可以使用System类的静态方法和静态域,而不必加类名前缀:

    out.println("abc");//System.out
    exit(0);//System.exit();
    
    sqrt(pow(x,2) + pow(y,2));//Math.sqrt(Math.pow(x,2) + Math.pow(y,2));
    

    (3)将类放入包中
    想要将一个类放入包中,就必须将包的名字放在源文件的开头,包中定义类的代码之前。

    package com.horstmann.corejava;
    public class Employee
    {
    	...
    }
    

    如果没有在源文件中放置package语句,这个源文件中的类就被放置在一个默认包中。默认包是一个没有名字的包。在此之前,我们定义的所有类都在默认包中。
    (4)包作用域
    如果没有指定publicprivate这个部分(类、方法或变量)可以被同一个包中的所有方法访问。
    (5)注释
    类注释必须放在import语句之后。
    每一个方法注释必须放在所描述的方法之前。
    @param变量描述:这个标记将对当前方法的参数部分添加一个条目。

  2. 类设计技巧
    (1)一定要保证数据私有
    (2)一定要对数据初始化
    (3)不要在类中使用过多的基本类型:用其他类代替多个相关的基本类型的使用。
    (4)不是所有的域都需要独立的域访问器和域更改器。 例如:员工的雇用日期是固定的,不可更改
    (5)将职责过多的类进行分解
    (6)类名和方法名要能够体现它们的职责
    (7)优先使用不可变的类。 如果多个线程试图同时更新一个对象,就会发生并发更改,其结果是不可预测的。

  3. 继承
    (1)子类

    public class manager extends Employee 
    {
    	添加方法和域
    }
    

    关键字extends标明正在构造的新类派生于一个已存在的类。已存在的类称为超类(superclass)基类(base class)或者父类(parent class);新类称为子类(subclass)派生类(derived class)或者孩子类(child class)
    尽管Employee类时一个超类,但并不是因为它由于子类或者拥有比子类更多的功能。实际上恰恰相反,子类比超类拥有的功能更加丰富。

    public class Manager extends Employee
    {
    	private double bonus;
    	...
    	public void setBonus(double bonus) {
    		this.bonus = bonus;
    	}
    }
    

    在通过扩展超类和定义子类的时候,仅需要之处子类与超类的不同之处。因此在设计类的时候,应该将通用的方法放在超类中,而将具有特殊用途的方法放在子类中。
    (2)覆盖方法
    子类覆盖超类中的方法

    public class Manager extends Employee {
    	public double getSalary() {
    	}
    }
    

    (3)子类构造器
    我们可以通过super实现对超类构造器的调用。使用super调用构造器的语句必须是子类构造器的第一语句。
    如果子类构造器没有显式的调用超类的构造器,则将自动地调用超类默认的构造器。如果超类没有不带参数的构造器,并且子类构造器中有没有显示地调用超类的其他构造器,则Java编译器将报错。
    关键字this有两个用途:一是引用隐式参数,二是调用该类其他的构造器。同样,super关键字也有两个用途:一是调用超类的方法,二是调用超类的构造器。
    一个对象变量可以指示多种实际类型的现象被称为多态,在运行时能够自动选择调用哪个方法的现象称为动态绑定
    (4)方法调用
    假设要调用x.f(args),隐式参数x声明为类C的一个对象。下面是调用过程的详细描述:
    1)编译器查看对象的声明类型和方法名。有可能存在多个名字为f,但参数类型不一样的方法。编译器将会一一列举所有C类中名为f的方法和其超类中访问属性为public且名为f的方法(超类的私有方法不可访问)。
    2)编译器将查看调用方法提供的参数类型。如果在所有名为f的方法中存在一个与提供参数类型完全匹配,就选择这个方法。这个过程称为重载解析
    ==方法的名字和参数列表称为方法的签名。如果在子类中定义了一个与超类签名相同的方法,那么子类中的这个方法就覆盖了超类中的这个相同签名的方法。不过,返回类型不属于签名的一部分,因此,在覆盖方法时,一定要保证返回类型的兼容性。
    3)如果是private方法、static方法、final方法或者构造器,那么编译器将可以准确地知道应该调用哪个方法,我们将这种调用方式成为静态绑定
    4)当程序运行,并采用动态绑定调用方法时,虚拟机一定调用与当前对象的实际类型最合适的那个类的方法。假设x的实际类型是D,它是C类的子类。如果D定义了方法f(String),那么就直接调用它;否则,将在D类的超类中寻找f(String),以此类推。
    覆盖一个方法时,子类方法不能低于超类方法的可见性。
    (5)阻止继承:final类和方法
    有时候,可能希望阻止人们利用某个类定义子类。不允许拓展的类称为final类。如果在定义类的时候使用了final修饰符就表明这个类时final类。类中的特定方法也可以声明为final。如果这样,子类不能覆盖这个方法(final类中的所有方法(不包括域)自动地成为final方法)。

    public class Employee {
    	public final String getName() {
    		return name;
    	}
    }
    

    (6)抽象类
    抽象类不能实例化。
    抽象方法充当着占位的角色,它们的具体实现在子类中。拓展抽象类可以有两种选择。一是在抽象类中定义部分抽象方法或不定义抽象类方法,这样就必须把子类也标记为抽象类;另一种是定义全部的抽象方法,这样一来,子类就不是抽象的了。
    抽象类的子类对象赋值给抽象类,那么抽象类可以调用抽象方法。
    例如:

    	public abstract class A {
    		public abstract void method();
    	}
    	public class B extends {
    		public void method() {
    			...
    		}
    	}
    	A a = (A)(new B());
    	a.method();
    

    (7)受保护访问
    任何声明为private的内容对其他类都是不可见的。这点对于子类也完全适用,即子类也不能访问超类的私有域。proteted可以允许子类访问
    (8)所有类的超类
    Object类是Java中所有类的始祖,在Java中每个类都由它拓展而来的。如果没有指明超类,Object就被人为是这个类的超类。可以使用Object类型的变量引用任何类型的对象:Object obj = new Employee();
    (9)equals
    equals用于检测一个对象是否等于另一个对象。在Object类中,这个方法将判断两个对象是否具有相同的引用。
    Java语言规范要求equals方法具有下面的特性。
    *自反性:对于任何引用xx.equals(x)应当返回true
    *对称性:对于任何引用xy。如果x.equals(y)返回truey.equals(x)也应该返回true
    *传递性:对于任何引用xyz,如果x.equals(y)返回truey.equals(z)返回ruex.equals(z)也应该返回true
    (10)ArrayList
    ArrayList在添加或删除元素时,具有自动调节数组容量的功能,而不需要为此编写任何代码。
    ArrayList是一个采用类型参数的泛型类。为了指定数组列表宝UN的元素对象类型,需要用一对尖括号将类名括起来加在后面,例如,ArrayList<Employee> staff = new ArrayList<Employee>();两边都使用类型参数有些繁琐:ArrayList<Employee> = new ArrayList();
    数组列表自动扩展容量的便利增加了访问元素语法的复杂度。其原因是ArrayList类并不是Java程序设计语言的一部分;它是一个由某些人编写且被放在标准库中的一个实用类。
    使用setget方法实现改变和访问数组元素的操作。set只能替换数组列表中已存在的元素,不能添加元素。
    (11)对象包装器与自动装箱
    有时,需要将int这个的基本类型转换为对象。所有的基本类型都有一个与之对应的类。
    例如,Integer类对应基本类型int。通常,这些类称为包装器(wrapper)。这些对象包装器类拥有很明显的名字:IntegerLongFloatDoubleShortByteCharacterVoidBoolean(前6个类派生于公共的超类Number)。对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。同时,对象包装器类还是final,因此不能定义它们的子类。

    ArrayList<Integer> list = new ArrayList();
    list.add(3);//会自动变换成下面的形式
    //list.add(Integer.valueOf(3)); 这种变换称为自动装箱
    int n = list.get(i);//会翻译成
    //int n = list.get(i).intValue();//这种形式称为自动拆箱
    
    //编译器将自动地插入一条对象拆箱的指令,然后进行自增计算,最后再将对象装箱
    Integer n = 3;
    n ++;
    

    大多数情况下,容易有一种假象,基本类型与它们的对象包装器是一样的,只是它们的相等性不同。==运算符也可以应用于对象包装器对象,只是检测的是对象是否指向同一个存储区域,因此,下面的比较通常不会成立:

    Integer a = 1000;
    Integer b = 1000;
    if(a == b) {
    	...
    }
    

    然而,Java实现确有可能让它成立。如果将经常出现的值包装到同一个对象中,这种比较就有可能成立。这种不确定的结果并不是我们所希望的。解决这个问题的办法是在两个包装器对象比较时调用equals方法。
    (12)参数数量可变的方法

    public class PrintStream {
    	public PrintStream printf(String fmt,Object...args){
    		return format(fmt, args);
    	}
    }
    

    (13)反射
    反射库提供了一个非常丰富且精心设计的工具集,以便编写能够动态操纵Java代码的程序。特别是在设计或者运行中添加新类时,能够快速地应用开发工具动态地查询新添加类的能力。
    能够分析能力的程序称为反射。反射机制功能极其强大,反射机制可以用来:
    *在运行时分析类的能力
    *在运行时查看对象,例如,编写一个toString方法供所有类使用
    *实现通用的数组操作代码
    *利用Method对象,这个对象很像C++中的函数指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值