自学Java第六章——《面向对象的基本特征》
面向对象的基本特征:
1、封装
2、继承
3、多态
6.1 封装
1、好处:
(1)隐藏实现细节,方便使用者使用
(2)安全,可以控制可见范围
2、如何实现封装?
通过权限修饰符
面试题:请按照可见范围从小到大(从大到小)列出权限修饰符?
修饰符 | 本类 | 本包 | 其他包的子类 | 任意位置 |
---|---|---|---|---|
private | √ | × | × | × |
缺省/省略 | √ | √ | × | × |
protected | √ | √ | √ | × |
public | √ | √ | √ | √ |
①权限修饰符可以修饰什么?
类(类、接口等)、属性、方法、构造器、内部类
②分别可以加什么权限修饰符?
类(外部类):public和缺省 如果前面有public,那必须文件名和类名一致。
属性:4种
方法:4种
构造器:4种
内部类:4种
3、通常属性的封装是什么样的?
当然属性的权限修饰符可以是private、缺省、protected、public。但是我们大多数时候,见到的都是private。
如果属性私有化了,然后给它们配上get/set方法。
set用于修改
get用于获取
示例代码:标准Javabean的写法
public class Student{
//属性私有化
private String name;
private int age;
private boolean marry;
//公共的get/set
public void setName(String n){
name = n;//这里因为还没有学习this等,可能还会优化
}
public String getName(){
return name;
}
public void setAge(int a){
age = a;
}
public int getAge(){
return age;
}
public void setMarry(boolean m){
marry = m;
}
public boolean isMarry(){//boolean类型的属性的get方法,习惯使用把get换成is
return marry;
}
}
6.2 构造器
1、构造器的作用: (1)和new一起使用创建对象
//调用无参构造创建对象
类名 对象名 = new 类名();
//调用有参构造创建对象
类名 对象名 = new 类名(实参列表);
(2)可以在创建对象的同时为属性赋值
public class Circle{
private double radius;
public Circle(){
}
public Circle(double r){
radius = r;//为radius赋值
}
}
2、声明构造器的语法格式:
【修饰符】 class 类名{
【修饰符】 类名(){//无参构造
}
【修饰符】 类名(形参列表){//有参构造
}
}
3、构造器的特点:
(1)所有的类都有构造器
(2)如果一个类没有显式/明确的声明一个构造器,那么编译器将会自动添加一个默认的无参构造
(3)如果一个类显式/明确的声明了构造器,那么编译器将不再自动添加默认的无参构造,如果需要,那么就需要手动添加
(4)构造器的名称必须与类名相同
(5)构造器没有返回值类型
(6)构造器可以重载
示例代码:
public class Circle{
private double radius;
public Circle(){
}
public Circle(double r){
radius = r;//为radius赋值
}
}
6.3 关键字this
1、this关键字:
意思:当前对象
(1)如果出现在构造器中:表示正在创建的对象
(2)如果出现在成员方法中:表示正在调用这个方法的对象
2、this的用法:
(1)this.属性
当局部变量与成员变量同名时,那么可以在成员变量的而前面加“this.”用于区别
(2)this.方法
调用当前对象的成员方法,完全可以省略“this.”
(3)this()或this(实参列表)
this()表示调用本类的无参构造
this(实参列表)表示调用本类的有参构造
this()或this(实参列表)要么没有,要么必须出现在构造器的首行
示例代码:
public class Student{
private String name;
private int score;
public Student(){
}
public Student(String name){
this.name = name;
}
public Student(String name, int score){
this(name);
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setScore(int score){
this.score = score;
}
public int getScore(){
return score;
}
}
3、成员变量与局部变量的区别?
这里只讨论实例变量(关于类变量见static部分)
(1)声明的位置不同
成员变量:类中方法外
局部变量:方法中或代码中
①方法的形参列表
②方法体中局部变量
③代码块中的局部变量
(2)运行时在内存中的存储位置不同
成员变量:堆
局部变量:栈
基本数据类型的变量在栈中,引用数据类型的变量在堆中:不准确
(3)修饰符
成员变量:有很多修饰符,例如:权限修饰符
局部变量:不能加权限修饰符,唯一的能加的是final
(4)初始化
成员变量:有默认值
局部变量:没有默认值,必须手动初始化
(5)生命周期
成员变量:随着对象的创建而创建,随着对象被回收而消亡,即与对象同生共死。每一个对象都是独立的。
局部变量:方法调用时才分配,方法运行结束就没有了。每一次方法调用,都是独立的
6.4 包
1、包的作用:
(1)可以避免类重名
有了包之后,类的全名称就变为:包.类名
(2)分类组织管理众多的类
例如:java.lang包,java.util包,java.io包.....
(3)可以控制某些类型或成员的可见范围
如果某个类型或者成员的权限修饰缺省的话,那么就仅限于本包使用
2、声明包的语法格式:
package 包名;
注意:
(1)必须在源文件的代码首行
(2)一个源文件只能有一个
3、包的命名规范和习惯: (1)所有单词都小写,每一个单词之间使用.分割 (2)习惯用公司的域名倒置
例如:com.atguigu.xxx;
建议大家取包名时不要使用“java.xx"包
4、使用其他包的类:
前提:被使用的类或成员的权限修饰符是>缺省的
(1)使用类型的全名称
例如:java.util.Scanner input = new java.util.Scanner(System.in);
(2)使用import 语句之后,代码中使用简名称
5、import语句
import 包.类名;
import 包.*;
注意:当使用两个不同包的同名类时,例如:java.util.Date和java.sql.Date。
一个使用全名称,一个使用简名称
示例代码:
package com.atguigu.test;
import java.util.Scanner;
public class Test{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
}
}
6.5 eclipse的使用
1、eclipse管理项目和代码的结构
workspace --> project --> 包-->类...
一个工作空间可以有多个项目。
2、快捷键
常规快捷键:
Ctrl + S:保存
Ctrl + C:复制
Ctrl + V:粘贴
Ctrl + X:剪切
Ctrl + Y:反撤销
Ctrl + Z:撤销
Ctrl + A:全选
eclipse中默认的快捷键:
Ctrl + 1:快速修复
Alt + /:代码提示
Alt + ?: Alt + Shift + / 方法的形参列表提示
Ctrl + D:删除选中行
Ctrl + Alt + ↓:向下复制行
Ctrl + Alt + ↑:向上复制行
Alt + ↓:与下面的行交换位置
Alt + ↑:与下面的行交换位置
Ctrl + Shift + F:快速格式
Ctrl + /:单行注释,再按一次取消
Ctrl + Shift + /:多行注释
Ctrl + Shift +\:取消多行注释
Shift + 回车:在光标下一行插入新航开始编辑
Ctrl + Shift + 回车:在光标上一行插入新航开始编辑
Alt + Shift + A:多行编辑 再按一次退出多行编辑模式
Alt + Shift + S:弹出自动生成代码的菜单选择,包括自动生成构造器、get/set、equals......
Ctrl + Shift + O:快速导包
Ctrl + Shift + T:打开某个类的源文件
Ctrl + O:打开某个类型的摘要outline
3、快速开发的代码模板
代码模板 + Alt + /
(1)main
public static void main(String[] args){
}
(2)sysout
System.out.println();
(3)for
for(int i=0; i<数组名.lenght; i++){
}
其他详细使用见《JavaSE柴林燕相关工具.docx》
6.6 面向对象的基本特征之二:继承
1、为什么要继承?继承的好处?(理解)
(1)代码的复用
(2)代码的扩展
2、如何实现继承?
语法格式:
【修饰符】 class 子类 extends 父类{
}
3、继承的特点
(1)子类会继承父类的所有特征(属性、方法)
但是,私有的在子类中是不能直接使用的
(2)子类不会继承父类的构造器
因为,父类的构造器是用于创建父类的对象的
(3)子类的构造器中又必须去调用父类的构造器
在创建子类对象的同时,为从父类继承的属性进行初始化用,可以借助父类的构造器中的代码为属性赋值。
(4)Java只支持单继承:一个子类只能有一个“直接”父类
(5)Java又支持多层继承:父类还可以有父类,特征会代代相传
(6)一个父类可以同时拥有很多个子类
6.7 关键字super
super关键字:引用父类的,找父类的xx
用法:
(1)super.属性
当子类声明了和父类同名的成员变量时,那么如果要表示某个成员变量是父类的,那么可以加“super.”
(2)super.方法
当子类重写了父类的方法,又需要在子类中调用父类被重写的方法,可以使用"super."
(3)super()或super(实参列表)
super():表示调用父类的无参构造
super(实参列表):表示调用父类的有参构造
注意:
(1)如果要写super()或super(实参列表),必须写在子类构造器的首行
(2)如果子类的构造器中没有写:super()或super(实参列表),那么默认会有 super()
(3)如果父类没有无参构造,那么在子类的构造器的首行“必须”写super(实参列表)
6.8 方法的重写
1、方法的重写(Override)
当子类继承了父类的方法时,又觉得父类的方法体的实现不适合于子类,那么子类可以选择进行重写。
2、方法的重写的要求
(1)方法名:必须相同
(2)形参列表:必须相同
(3)修饰符
权限修饰符: >=
(4)返回值类型
如果是基本数据类型和void:必须相同
如果是引用数据类型:<=
在Java中我们认为,在概念范围上:子类 <父类
3、重载(Overload)与重写(Override)的区别
重载(Overload):在同一个类中,方法名相同,形参列表不同,和返回值类型无关的两个或多个方法。
重写(Override):在父子类之间。对方法签名的要求见上面。
特殊的重载:
public class TestOverload {
public static void main(String[] args) {
B b = new B();
//b对象可以调用几个a方法
b.a();
b.a("");//从b对象同时拥有两个方法名相同,形参不同的角度来说,算是重载
}
}
class A{
public void a(){
//...
}
}
class B extends A{
public void a(String str){
}
}
6.9 非静态代码块
1、语法格式
【修饰符】 class 类名{
{
非静态代码块
}
}
2、作用
目的:在创建的过程中,为对象属性赋值,协助完成实例初始化的过程
3、什么时候执行?
(1)每次创建对象时都会执行
(2)优先于构造器执行
6.10 实例初始化过程
1、概念描述
-
实例初始化过程:实例对象创建的过程
-
实例初始化方法:实例对象创建时要执行的方法
-
实例初始化方法的由来:它是有编译器编译生成的
-
实例初始化方法的形式:<init>()或<init>(形参列表)
-
实例初始化方法的构成:
①属性的显式赋值代码
②非静态代码块的代码
③构造器的代码
其中
①和②按顺序执行,从上往下
③在①和②的后面
因此一个类有几个构造器,就有几个实例初始化方法。
2、单个类实例初始化方法
示例代码:
class Demo{
{
System.out.println("非静态代码块1");
}
private String str = assign();//调用方法,来为str进行显式赋值
public Demo(){
System.out.println("无参构造");
}
public Demo(String str){
this.str = str;
System.out.println("有参构造");
}
{
System.out.println("非静态代码块2");
}
public String assign(){
System.out.println("assign方法");
return "hello";
}
}
图解:
3、父子类的实例初始化
注意:
(1)原先super()和super(实参列表)说是调用父类的构造器,现在就要纠正为调用父类的实例初始化方法了
(2)原先super()和super(实参列表)说是必须在子类构造器的首行,现在要纠正为必须在子类实例初始化方法的首行
结论:
(1)执行顺序是先父类实例初始化方法,再子类实例初始化方法
(2)如果子类重写了方法,通过子类对象调用,一定是执行重写过的方法
示例代码:
class Ba{
private String str = assign();
{
System.out.println("(1)父类的非静态代码块");
}
public Ba(){
System.out.println("(2)父类的无参构造");
}
public String assign(){
System.out.println("(3)父类的assign()");
return "ba";
}
}
class Er extends Ba{
private String str = assign();
{
System.out.println("(4)子类的非静态代码块");
}
public Er(){
//super() ==>调用父类的实例初始化方法,而且它在子类实例初始化方法的首行
System.out.println("(5)子类的无参构造");
}
public String assign(){
System.out.println("(6)子类的assign()");
return "er";
}
}
class Test{
public static void main(String[] args){
new Er();//612645
}
}
图解:
6.11 面向对象的基本特征之三:多态
1、多态:
语法格式:
父类 引用/变量 = 子类的对象;
2、前提:
(1)继承
(2)方法的重写
(3)多态引用
3、现象:
编译时看左边/"父类",运行时看右边/"子类"。
编译时,因为按父类编译,那么只能父类有的方法,子类扩展的方法是无法调用的;
执行时一定是运行子类重写的过的方法体。
示例代码:
class Person{
public void eat(){
System.out.println("吃饭");
}
public void walk(){
System.out.println("走路");
}
}
class Woman extends Person{
public void eat(){
System.out.println("细嚼慢咽的吃饭");
}
public void walk(){
System.out.println("婀娜多姿走路");
}
public void shop(){
System.out.println("买买买...");
}
}
class Man extends Person{
public void eat(){
System.out.println("狼吞虎咽的吃饭");
}
public void walk(){
System.out.println("大摇大摆的走路");
}
public void smoke(){
System.out.println("吞云吐雾");
}
}
class Test{
public static void main(String[] args){
Person p = new Woman();//多态引用
p.eat();//执行子类重写
p.walk();//执行子类重写
//p.shop();//无法调用
}
}
4、应用:
(1)多态参数:形参是父类,实参是子类对象
(2)多态数组:数组元素类型是父类,元素存储的是子类对象
示例代码:多态参数
class Test{
public static void main(String[] args){
test(new Woman());//实参是子类对象
test(new Man());//实参是子类对象
}
public static void test(Person p){//形参是父类类型
p.eat();
p.walk();
}
}
示例代码:多态数组
class Test{
public static void main(String[] args){
Person[] arr = new Person[2];//多态数组
arr[0] = new Woman();
arr[1] = new Man();
for(int i=0; i<arr.length; i++){
all[i].eat();
all[i].walk();
}
}
}
5、向上转型与向下转型:父子类之间的转换
(1)向上转型:自动类型转换
当把子类的对象赋值给父类的变量时(即多态引用时),在编译时,这个对象就向上转型为父类。此时就看不见子类“特有、扩展”的方法。
(2)向下转型:强制转换。有风险,可能会报ClassCastException异常。
当需要把父类的变量赋值给一个子类的变量时,就需要向下转型。
要想转型成功,必须保证该变量中保存的对象的运行时类型是<=强转的类型
示例代码:
class Person{
//方法代码省略...
}
class Woman extends Person{
//方法代码省略...
}
class ChineseWoman extends Woman{
//方法代码省略...
}
public class Test{
public static void main(String[] args){
//向上转型
Person p1 = new Woman();
//向下转型
Woman m = (Woman)p1;
//p1变量中实际存储的对象就是Woman类型,和强转的Woman类型一样
//向上转型
Person p2 = new ChineseWoman();
//向下转型
Woman w2 = (Woman) p2;
//p2变量中实际存储的对象是ChineseWoman类型,强制的类型是Woman,ChineseWoman<Woman类型
}
}
6、instanceof
表达式语法格式:
对象/变量 instanceof 类型
运算结果:true 或 false
作用:
用来判断这个对象是否属于这个类型,或者说,是否是这个类型的对象或这个类型子类的对象
示例代码:
class Person{
//方法代码省略...
}
class Woman extends Person{
//方法代码省略...
}
class ChineseWoman extends Woman{
//方法代码省略...
}
public class Test{
public static void main(String[] args){
Person p = new Person();
Woman w = new Woman();
ChineseWoman c = new ChineseWoman();
if(p instanceof Woman){//false
}
if(w instanceof Woman){//true
}
if(c instanceof Woman){//true
}
}
}