修饰符
权限修饰符
public、protected、默认修饰符、private 的访问权限:
权限修饰符 | public | protected | 默认(void) | private |
---|---|---|---|---|
本类中 | Yes | Yes | Yes | Yes |
同一包下(子类和无关类) | Yes | Yes | Yes | No |
不同包下的子类 | Yes | Yes | No | No |
不同包下的无关类 | Yes | No | No | No |
状态修饰符
static
、final
抽象修饰符
abstract
类
- 权限修饰符:public、默认修饰符
- 状态修饰符:final
- 抽象修饰符:abstract
- 用的最多的是public
成员变量
- 权限修饰符:public、protected、默认修饰符、private
- 状态修饰符:static、final
- 用的最多的是private
构造方法
- 权限修饰符:public、protected、默认修饰符、private
- 用的最多的是public
成员方法
- 权限修饰符:public、protected、默认修饰符、private;
- 状态修饰符:static、final;
- 抽象修饰符:abstract
- 用的最多的是public
关键字
final
-
修饰类
当用final 修饰一个类的时候,表明这个类不能被继承 -
修饰方法
被final 修饰的方法不能被子类重写(可以重载)
如果父类中final修饰的方法同时访问控制权限为private,将会导致子类中不能直接继承到此方法,因此,此时可以在子类中定义相同的方法名和参数,此时子类的方法不是重写了基类的方法,而是在子类中重新定义了新的方法。 -
修饰变量
被final修饰的成员变量 表示常量,只能被赋值一次,赋值后,值不再改变。- 对于基本类型,final 使数值不变;
- 对于引用类型,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。
public class Test {
public static void main(String[] args) {
final MyClass myClass = new MyClass();
System.out.println(++myClass.i); // i = 1
}
}
class MyClass {
public int i = 0;
}
static
静态变量
- 静态变量:又称为类变量,也就是说静态变量属于类,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量随着类的加载完成初始化,它在内存中仅有一个,且JVM也只会为它分配一次内存。
- 实例变量(没有被static修饰的变量):与静态变量不同,它是伴随着实例的,没创建一个实例就会产生一个实例变量,它与该实例同生共死。
public class A {
private int x; // 实例变量
private static int y; // 静态变量
public static void main(String[] args) {
// int x = A.x; //这个语句是错误的,实例变量不能通过类名来调用
A a = new A();
int x = a.x;
int y = A.y; //或者 int y = a.y
}
}
静态方法
由于静态方法不依赖于任何对象就可以进行访问,所以静态方法必须有实现,也就是说它不能是抽象方法,而且对于静态方法来说,是没有this
的。因为这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法。非静态成员变量 / 方法都是必须依赖具体的对象才能够被调用。但是在非静态成员方法中是可以调用静态成员方法和变量的。
静态语句块
静态语句块只在类初始化时运行一次,因此静态代码块可以用来优化程序性能。
public class A {
static {
System.out.println("123");
}
public static void main(String[] args) {
A a = new A();
}
}
//运行代码,输出 123
静态导包
在使用静态变量和方法时,不用再指明ClassName,从而简化代码,但可读性大大降低。
import static com.xxx.ClassName.*
初始化顺序
静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。
public static String staticField = "静态变量";
static {
System.out.println("静态语句块");
}
public String field = "实例变量";
{
System.out.println("普通语句块");
}
然后才是构造函数的初始化
public InitialOrderTest() {
System.out.println("构造函数");
}
如果存在继承关系,初始化顺序为:
- 父类 静态变量 和 静态语句块
- 子类 静态变量 和 静态语句块
- 父类 实例变量 和 普通语句块
- 父类 构造函数
- 子类 实例变量 和 普通语句块
- 子类 构造函数
Object的通用方法
equals()
public boolean equals(Object obj)
等价与相等 / equals 与 ==
- 对于基本类型, == 判断两个值是否相等,基本类型没有equals()方法
- 对于引用类型
- == 判断两个变量是否引用同一个对象(所指向的对象地址)
- 如果没有对 equals() 方法进行重写,则比较的是引用类型的变量所指向的对象地址;
- 比如String等类重写了 equals()方法 比较的是所指向的对象的内容
hashCode()
public native int hashCode()
由方法声明可知,该方法返回一个int类型的数值,并且是本地方法,因此,在Object类中并没有给出具体的实现。
Hash,一般翻译做散列、杂凑,或音译为哈希
hashCode()方法的主要作用是为了配合基于散列集合一起正常运行,这样的散列集合包括HashSet、HashMap 以及 HashTable。
散列集合使用hashCode方法来计算对象应该存储的位置,因此要将对象添加到这些集合类中,需要让对应的类实现 hashCode()方法。
当向集合中插入对象时,如何判断该对象是否已经存在,此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对象的哈希值,通过判断,如果集合中没有该哈希值,它就可以直接存进去,不用再进行任何比较。如果存在该哈希值,就调用它的equals方法 与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。所以这里存在一个冲突解决的问题,这样一来实际调用equals方法的次数就大大降低了。
Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值
//new两个等价对象
EqualExample e1 = new EqualExample(1, 1, 1);
EqualExample e2 = new EqualExample(1, 1, 1);
System.out.println(e1.equals(e2)); // true
//此EqualExample类没有实现 hashCode()方法,因此这两个对象的哈希值是不同的
HashSet<EqualExample> set = new HashSet<>();
set.add(e1);
set.add(e2);
//最终导致集合中添加了两个等价对象
System.out.println(set.size()); // 2
hashCode() 返回的是哈希值,而equals() 是用来判断的。等价的两个对象散列值一定相同,但散列值相同的两个对象不一定等价。
在重写 equals()方法时,应当总是重写 hashCode() 方法,保证等价的两个对象哈希值也相等。
clone()
下面这篇文章写的很棒,通俗易懂。
Java如何复制对象
继承
抽象类
抽象方法
抽象方法是一种特殊的方法,它只有声明,没有具体实现。
abstract void method();
- 抽象方法必须用
abstract
关键字修饰 - 如果一个类含有抽象方法,则这个类为抽象类
抽象类
抽象类必须在类前面用 abstrac 关键字修饰
- 因为抽象类中含有无具体实现的方法(抽象方法),所以不能用抽象类来创建对象。
- 一个抽象类中不一定必须含有抽象方法
public abstract class ClassName {
abstract void method();
}
抽象类和普通类的最大区别就是,抽象类不能被实例化,只能被继承。
接口
接口(interface),它是对行为的抽象。
public interface InterfaceName {
}
特点
- 在Java 1.8之前,它可以看成是一个完全抽象的类,也就是说,他不能有任何方法的实现。
- Java 1.8开始,接口也可以有默认的实现方法。
- 接口中的变量会被隐式的指定为
public static finale
变量,用 private 修饰会报编译错误 - 接口中的方法会被指定为
public abstract
方法,其它修饰也会报编译错误
抽象类与接口的区别
- 一个类可以实现多个接口,但只能继承一个抽象类
- 接口的字段(变量)只能是static 和 finale 类型的,而抽象类的字段没有这种限制
- 接口的成员只能是public 的,而抽象类的成员可以有多种访问权限
super
- 访问父类的构造方法,从而委托父类完成一些初始化工作
- 访问父类的成员,如果子类重写父类的某个方法,可以使用
super
关键字来引用父类的方法实现
//父类
public class SuperExample {
protected int x;
protected int y;
public SuperExample(int x, int y) {
this.x = x;
this.y = y;
}
public void func() {
System.out.println("SuperExample.func()");
}
}
//子类
public class SuperExtendExample extends SuperExample {
private int z;
public SuperExtendExample(int x, int y, int z) {
super(x, y); //使用super访问父类成员变量
this.z = z;
}
@Override
public void func() {
super.func();//使用super访问父类成员方法
System.out.println("SuperExtendExample.func()");
}
}
重写 与 重载
重写存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的方法。
使用 @Override
注解,可以让编译器检查是否满足重写的限制条件。
重载存在于同一个类中,指一个方法与一个已经存在的方法名称上相同,但是参数类型、个数和顺序至少有一个不同。
需要注意的是,返回值不同,其它都相同 不算是重载。
文章学习自 CYC