主要讨论类的一些知识,Java比较繁琐的反射、lambda以及内部类不做涉及,这些东西要讲太复杂也太多了。
目录
- 关于类的基础常识
- 类关系
- Java访问修饰符
- static在类中的应用
- 文档注释
- 阻止继承: final 类和方法
- 常识与建议
- 接口
- 基础知识
- 默认方法
- 拷贝
关于类的基础知识
类关系
类和类之间关系包括了 is a,has a,use a,三种关系
is a包括了继承,实现关系,感觉是当中最重要的一个关系
has a包括了组合,关联,聚合关系,表示A的对象包含B的对象。
use a包括了 依赖关系,依赖关系的话,比较不推荐,就是说一个类的方法操纵另一类的对象,即为依赖
关于它们的UML具体看 UML类图关系(泛化 、继承、实现、依赖、关联、聚合、组合)
不过简单来讲,主要还是用到继承(直线+三角形)、接口实现(虚线+三角形)以及聚合关系(棱形+直线),注意方向。
Java访问修饰符
- private-仅对本类可见
- public-对所有类可见
- protected-对本包和所有子类可见
- 默认无-本包可见
static在类中的应用
静态域
静态域,即该类所有实例共享该域,比如类A有两个实例 A_1,A_2,两者的id域各有一个空间,但是count域共享。
public class A
{
private static int count=0;
private int id;
...
}
静态常量
下面定义的volume就是静态常量啦,常量是由final决定的,但是static保证所有实例共用一个,当然没有也没啥,就浪费空间呗,而且使用static就可以用A.volume直接访问,不需要实例化。注意,是public域哦!
public class A
{
private static int count=0;
private int id;
public static final int volume=502;
...
}
静态方法
静态方法不许向对象实施操作。比如Math类的pow方法,计算时,将不会使用Math对象,说白了就是这个方法和这个类几乎没啥联系,并不访问实例域。我们可以直接调用A.getVolume(),不需要实例化,因为静态方法的特殊性,一般认为静态方法不会破坏对象,也就是比较安全的。
public class A
{
private static int count=0;
private int id;
public static final int volume=502;
...
public static getVolume(){
return volume;
}
}
工厂方法
比如NumberFormat类使用工厂方法产生不同的类对象。
NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();
NumberFormat percentFormatter = NumberFormat.getPercentInstance();
当使用构造器的时候,是完全没有办法这么创建的哦。
不需要使用对象调用静态方法,比如pow,比如main。
文档注释
一般的不说,比如\\
,比如\**\
这两种
说一下 \**...*\
这种
通用注释
/**
* @author 作者
* @since 起始版本
* @version 版本
* @deprecated (可选)
* @see [类、类#方法、类#成员],也可以跟标签,使用""的话,内容会被显示在see also上,可以有多个see also
* {@link 规则同"@see"标记}
*/
//具体例子
/**
* @author CapybaraJ
* @since version 0.1.1
* @version 0.1.3
* @deprecated Use getVolume_2(int)
instead
* @see com.hostmann.corejava.A#getVolume()
* {@link #getVolume_2(int)}
*/
类注释
/**
* 类注释呢,用语句解释清楚这个类干嘛的
* 可以分行的,也可以使用html标签
* 虽然我没用过。。。
* 每一行前面的*号可以没有,但是一般ide都会自己给你补
*/
方法注释
/**
* 简要描述这方法干什么的
* @param param1 解释param1代表啥balabala
* @param param2 解释param2代表啥balabala
* @return 返回值说明
* @exception/throws 异常类型 异常说明
*/
阻止继承: final 类和方法
不允许扩展的类被称为 final 类 。 如果在定义类的时候使用了 final 修饰符就表明这个类是 final 类 。 例如 , 假设希望阻止人们定义Executive 类的子类, 就可以在定义这个类的时候 使用 final 修饰符声明 。 声明格式如下所示
public final class Executive extends Manager
{
...
}
类中的特定方法也可以被声明为 final 。 如果这样做 , 子类就不能覆盖这个方法, 例如:
public class Employee
{
...
public final String getName()
{
return name;
}
}
将方法或类声明为 final 主要目的是 : 确保它们不会在子类中改变语义 。 例如 , Calendar类中的 getTime 和 setTime 方法都声明为 final 。 这表明 Calendar 类的设计者负责实现 Date 类与日历状态之间的转换 , 而不允许子类处理这些问题 。 同样地, String 类也是 final 类, 这意味着不允许任何人定义 String 的子类 。 换言之 , 如果有一个 String 的引用, 它引用的一定是
一个 String 对象 , 而不可能是其他类的对象 。
抽象类
常识与建议
- 一个对象变量并没有实际包含一个对象,而仅仅引用一个对象。所以传参时,如果是一个基础型变量,比如int,那么不会受到影响,但如果参数是一个类的引用,那么修改会直接作用到类上。
- 一般对数据域采用getter/setter方法,并将数据域标记为private。
- final实例域大多应用在基本类型域以及不可变类的域上,比如String类哇。final数据域必须初始化且无法更改。
- final类和方法将会阻止继承,当然,类和方法被声明为final,域却不是哦。
- 建议使用
类名.静态方法
的方法来调用静态方法,而不使用类实例。 - 子类不能访问超类的私有域。
- 子类的对象可以赋值给超类,而超类的对象不能赋值给子类,原因很简单,不能让子类对象操作超类实例,会出事。
- 子类数组的引用可转换为超类数组的引用,但是也会有严重问题,因此不要随便转换。
//经典职员-经理例子
Manager[] managers = new Manager[10];
Employee[] staff = managers; //OK
staff[0] = new Employee(...); //woc,居然编译通过了 ERROR!!!
//要知道,staff[0]的地址也是managers[0]的地址,我靠,一个普通员工成了mananger!!!
- 包含一个或者多个抽象方法的类本身必须被声明为抽象的,但是抽象类不一定包含抽象方法。抽象方法再被extends时必须要被实现。
- 抽象类虽然不能实例化,但是可以引用非抽象子类的实例。
- 有时, 需要将 int 这样的基本类型转换为对象 。通常 , 这些类称为包装器 ( wrapper ) 。共有:Integer 、 Long 、 Float 、 Double 、 Short 、 Byte 、 Character 、 Void 和 Boolean ( 前6 个类派生于公共的超类 Number ) 。 对象包装器类是不可变的。举例:
ArrayList
//wrong
ArrayList
list= new ArrayList <> () ; //right
接口
基础知识
接口不是类 , 而是对类的一组需求描述, 这些类要遵从接口描述的统一格式进行定义 。接口中的所有方法自动地属于 public 。 因此, 在接口中声明方法时 , 不必提供关键字public
,不过 , 在实现接口时, 必须把方法声明为 public
。接口绝不能含有实例域!
例子:
//在 JavaSE 5.0 中, Comparable 接口已经改进为泛型类型 。
public interface Comparable < T >
{
int compareTo (T other) ; // parameter has type T
}
在实现 Comparable < Employee > 接口的类中 , 必须提供下列方法
int coirpareTo(Employee other)
class Employee implements Comparable
{
public int compareTo(Employee other)
{
return Double.compare(salary , other.salary);
}
}
接口不是类, 尤其不能使用 new 运算符实例化一个接口 :
x = new Comparable( . . . ) ; // ERROR
然而, 尽管不能构造接口的对象, 却能声明接口的变量 :
Comparable x ; // OK
接口变量必须弓 I 用实现了接口的类对象 :
x = new Employee ( ... ) ; //OK provided Employee implements Comparable
接下来, 如同使用 instanceof 检查一个对象是否属于某个特定类一样, 也可以使用instance 检查一个对象是否实现了某个特定的接口 :
if ( anObject instanceof Comparable ) { . . . }
尽管每个类只能够拥有一个超类, 但却可以实现多个接口 。使用逗号将实现的各个接口分隔开 。
默认方法
可以为接口方法提供一个默认实现 。 必须用 default 修饰符标记这样一个方法 。这样我就不用所有的都覆盖,只用对我需要的覆盖。
比如鼠标捕捉事件:
如果先在一个接口中将一个方法定义为默认方法 , 然后又在超类或另一个接口中定义了同样的方法, 会发生什么情况 ? 诸如 Scala 和 C + + 等语言对于解决这种二义性有一些复杂的规则 。 幸运的是, Java 的相应规则要简单得多 。 规则如下 :
1 ) 超类优先 。 如果超类提供了一个具体方法 , 同名而且有相同参数类型的默认方法会被忽略 。
2 ) 接口冲突 。 如果一个超接口提供了一个默认方法 , 另一个接口提供了一个同名而且参数类型 ( 不论是否是默认参数 ) 相同的方法, 必须覆盖这个方法来解决冲突 。
拷贝
Cloneable 接口 , 这个接口指示一个类提供了一个安全的 clone 方法 。 由于克隆并不太常见 , 而且有关的细节技术性很强 ,所以不细讲。但是必须明确浅拷贝和深拷贝的概念。看图就很直白了。