返回顶部

自学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
           
        }
    }
}

 

posted @ 2020-02-12 20:18  可乐葱花  阅读(256)  评论(0编辑  收藏  举报