面向对象的3大特征 -- 封装、继承、多态
==封装
-- 合理隐藏:隐藏不想被外界操作的field(成员)、方法、构造器
-- 合理暴露:一般是希望被外界调用的方法
实现隐藏的4个修饰符:(访问权限由小到大)
private -- 当前类访问权限(私有)
默认(无修饰符)-- 包访问权限
protected -- 子类访问权限(保护,父类可访问)
public -- 公共访问权限
说明:
private -- 专门用于隐藏field
默认 -- 修饰希望在同一个包中被访问的成员
protected -- protected修饰的方法,通常是希望被它的子类访问
public -- 专门用于暴露方法
对于局部变量,它仅在方法内甚至是仅在代码块内有效,作用域很小,所以用访问修饰符修饰时多余的.
包
-- 包的作用就是“命名空间”的作用
-- Java要求一个类的完整类名,必须是“包名 + 类名”
定义包的语法:
package 包1.包2.包N;
-- 该语句在一个源文件中最多出现一次,一旦在java源文件中定义了package,则该源文件中所有的Java类,都位于该包下
将一个类放入一个包中有2个要求:
1. 在该类的源代码中使用package开声明包
2. 生成的class文件必须放在相应文件结构下
包的导入: 用import 导入包下的类
import 包.*; ( * 只能代表类 )
import static 包.*; ( * 只能代表静态filed和静态方法)
1.一般的import导入的作用是是省略包
2.静态导入 import static -- 导入指定类下的静态成员,从而省略类
相关命令:
javac -d 选项 -- 可保证将生成的class文件放到package所对应的的文件结构下
==继承
Java的继承 -- 一个类与另一个类的关系
类与类之间的关系:从“一般”到“特殊”的关系
Java的继承是单继承,只有一个直接父类(extends 后面只能有一个父类),但是可以有 n 个间接父类;
如果没有显式定义父类,系统会默认该类继承 Object 类 -- 一切类都是 Object 类的子类。
子类扩展了父类,将获得父类的全部属性(Field)和方法,但不能获得构造器。
★ 方法重载(Overload) :两同,以不同
★ 方法重写(Override,也叫覆盖) :两同,两小,一大
-- 重写 :子类从父类继承的方法不能满足子类的需要,子类可以重写父类的方法
-- 两同 :方法名相同,形参表列相同
-- 两小 :子类重写的方法的返回值类型必须必父类方法的返回值类型小或相等
子类重写方法声明抛出的异常必须必父类方法声明抛出的异常 小或相等
-- 一大 :子类重写的方法的访问权限必须必父类方法的访问权限更大或相等
-- @Override 注释:放在重写方法的上面一行,作用是让编译器执行更严格的检查,要求被修饰的方法必须是重写父类的方法
-- 只有方法才能被覆盖,变量没有覆盖的说法
★ super 关键字
-- super 限定 :强制去调用父类的方法
-- super 调用 :用于显式调用父类的构造器
【说明】
1.在创建一个子类实例时,总会调用最顶层的父类的构造器 一次
2.子类构造器总会调用父类构造器,如果子类构造器没有显式的使用super调用父类构造器,子类构造器会默认调用父 类的无参构造器
3.super调用和this调用类似,调用的格式一样,同时,二者都必须出现在构造器的第一行,所以二者不能同时出现
【注意】
1.如果既没有super调用,也没有this调用,子类构造器会 默认调用父类的无参构造器
2.如果有super调用,子类构造器会根据super调用所传入的 参数去匹配调用父类的构造器
3.如果有this调用,子类构造器会先找this调用所对应的方法中内重载的构造器,如果找不到,则在根据参数匹配去调用本类中的构造器
== 多态(ploymorphism)
由于有如下关系:
父类到子类的关系 -- 从一般到特殊的关系
因此,子类的实例完全可以当成父类的对象使用,即父类的引用变量完全可以指向子类的实例
**多态 -- 同一个类型的变量,在执行同一个方法时,表现出多种行为特征就是多态。
Java 引用变量的两个类型:
编译时类型: 由声明它的类型类决定;
运行时类型: 由该引用变量实际指向的对象来决定
当调用引用变量时,它总是呈现出 运行时类型 的行为特征
在编译阶段,编译器并不知道该引用变量实际所引用对象的类型,编译器只能识别其 编译时类型。
经典例子分析:
class A
{
int count = 20;
}
class B extends A
{
int count = 200;
}
public class Test
{
public static void main(String[] args)
{
A a = new A();
System.out.println(a.count);
B b = new B();
System.out.println(b.count);
A ab = b; //向上转型
System.out.println(ab.count);
}
}
运行结果 :20
200
20
结果分析:
前两行的输出毫无疑问,问题在
A ab = b;
System.out.println(ab.count);
的输出是20,而不是200;在这之间我们可以用
System.out.println(ab == b);
来进行简单的判断,结果输出为 true ,说明 ab 和 b 两个引用变量指向同一个实例,既然 ab 和 b 指向同一个实例,为什么输出的是20不是200呢?原因在于:
1.对于 class A 和class B来说,class B是class A的子类,由于子类的变量并不会覆盖父类的变量,所以实际上在class B中是存在来两个count,在这分别记作 A.count 和B.count ;
2.虽然在 class B中存在A.count 和B.count ,但是究竟输出那一个 count ,取决于该引用变量的声明时类型,此处 声明时类型 是 class A,所以输出 20 即A.count ,同理若改为 B ab = b ;则输出 200 即 B.count ;
强制类型转换的运算符是: ( 类型 )
1.基本类型之间(除了 boolean 之外),都可以进行转换;
2.引用类型之间,只能在具有集成关系的两个类型之间才能转换,否则编译报错。
Example :
public class ClassCast
{
public static void main(String[] args)
{
Object ob = "Crystal"; //向上转型
/*
1.Object和System之间有继承关系,所以可以通过编译
2.由于ob的运行时类型是String,不是System,所以强转会引发 ClassCastException (类型转换异常)
*/
System sys = (System) ob;
System.out.println(ob);
}
}
instanceof 运算符 -- 判断前面变量所引用的对象,是否为后面类型的实例
【注意】
instanceof 前面操作数的类型要么与后面的类相同,要么与后面类有父子集成关系,否则编译报错。
==初始化块
类中可以拥有的成分:
1.field
2.方法
3.构造器
4.初始化块 -- 没有名字
5.内部内/枚举/接口
执行性语句(循环、分支、创建对象、赋值、调用方法)只能放在方法、构造器、初始化块。
★初始化块的语法:
[修饰符]{
//可执行性的语句
}
修饰符 -- 只能是 static
-- 有 static,说明是类初始化块/静态初始化块
-- 没有static,说明是实例初始化块/非静态初始化块
由于 初始化块 没有名字,所以只能被“隐式”执行
★实例初始化块
1.实例初始化块的代码,会在 每次调用构造器之前 被隐式执行
2.Java允许提供多个实例初始化块,但是没有多大意义,所以一般合并为一个
3.JDK编译器会把 实例初始化块的代码 、声明 Field 时指定的初始化的代码 都提取到构造器的 “最前面”
【实例初始化的代码】 与 【声明 Field 时指定初始化的代码】 按其在源代码中的顺序
4.JDK编译器会把 实例初始化块的代码 、声明Field时指定初始化的代码 都提取到构造器的 “最前面”
★类初始化
当程序第一次主动使用该类就会初始化该类。以下5中情况会初始化该 :
-- 访问该类静态 Field 或静态方法
-- 初始化了该类的一个子类(因为Java初始化一个类,永远先从最顶层父类Object 开始初始化)
-- 使用反射 Class.forName( 类名 )
-- 该类作为主类使用
-- 使用该类来创建对象
典型的:使用类来声明变量,并不算主动使用该类,所以它不会初始化该类
★类初始化块
-- 负责对类进行初始化,当类被加载后,对类初始化后被隐式执行
-- 一个 JVM,对一个类,只初始化一次,因此类初始化只有一次执行机会,而实例初始化可以执行N次
★Java 提供的基本类型的包装类
Java是面向对象的语言,在Java里面,一切都是对象,所有类都是 Object 类的子类。
对于8个基本类型是例外,因为8个基本类型是来自 C语言,8个基本类型不能当做对象使用,于是 Java 为8个基本类型 提供了相应的包装类,但8个基本类型 的值(本质)依然是8个基本类型,只是其被包装过了,因此可以被当成对象使用,包装如下:
基本类型 → 包装类
byte → Byte
short → Short
int → Integer
long → Long
float → Float
double → Double
char → Character
boolean → Boolean
从JDK1.5之后,基本类型的值可以直接当成对象使用
JDK1.5 提供了“自动装箱(auto box” -- 基本类型的值,可以自动被当成它的包装类使用(可以自动当成对象使用,也可以作为对象传入方法)
由于从JDK1.5 开始,提供了自动装箱、自动拆箱
-- 基本类型的值,可以自动被当成其包装类实例(即对象)使用(即自动装箱)
-- 反过来,包装类的实例(对象)也可以自动当成基本类型的值使用(即自动拆箱)
总结:用包装类的实例更好更方便