[笔记] 疯狂JAVA讲义(第3版)第5章 面向对象(上)

简介: [笔记] 疯狂JAVA讲义(第3版)第5章 面向对象(上)

第5章 面向对象(上)

5.1 类和对象

类可以当成是一种自定义类型,可以使用类来定义变量。这种类型变量称为引用变量。

5.1.1 定义类

[修饰符] class 类名

{

构造器

成员变量

方法

}


各成员之间的定义顺序没有任何影响,各成员之间可以相互调用。

@static成员不能调用非static 成员。

成员变量用于定义类包含的状态数据,方法用于定义类的行为或功能实现。构造器用于构造类的实例,java通过new调用构造器,返回类的实例。


定义成员变量的语法:

[修饰符] 类型 成员变量名 [=默认值];

定义方法的语法:

[修饰符] 返回值类型 方法名(形参列表)

{//方法体}

定义构造器的语法

[修饰符] 构造器名(形参列表)

{}

构造器名必须和类名相同

@static 是一个特殊的关键字,static修饰的成员表明它属于这个类本身,而不属于类的单个实例。

定义一个Person类:

public class Person

{

    public String name;

    public int age;

 

    public void say(String content){

        System.out.println(content);

    }

}

Person没有定义构造器,系统为它提供默认构造器。

 

5.1.2 对象的产生和使用

创建对象的途径是构造器,通过new即可调用构造器来创建实例。

Person p;

p = new Person();

 

@可以简写为 Person p = new Person();

使用对象:

1、访问对象的实例变量

2、调用对象的方法

p.name = “李刚”;

p.say(“Java很简单”);

@static修饰的方法和变量,既可以通过类来调用,也可以通过实例来调用。

5.1.3 对象、引用和指针

和数组类似,类也是一种引用类型。

Person p2 = p; 其实是将p的地址赋值给p2,p2和p指向的是同一个Person对象。

5.1.4 对象的this引用

this关键字总是指向调用该方法的对象。

this的使用有两种情形:

1、构造器中引用该构造器正在初始化的对象

2、在方法中引用调用该方法的对象。

this最大的作用就是让类中的一个方法,访问该类的另一个方法或实例变量。

 

假设有一个Dog类

Dog类有jump()方法,现在定义一个run()方法,要在run()方法中调用jump()

public void run(){

this,jump();

}

@java允许对象的一个成员直接调用另一个成员,可以省略this前缀

 

当方法中的某个局部变量和成员变量同名时,要使用被覆盖的成员变量,就要使用this前缀。

public Test{
public int foo;
public Test{
int foo = 0;
this.foo = 6;
}
}

5.2 方法详解

方法是类或对象的行为特征的抽象。从功能上看,方法完全类似于传统结构化程序设计中的函数。

5.2.1 方法的所属性

方法是类和对象的附属。体现在:

1、方法不能独立定义,只能在类中定义

2、从逻辑上看,方法要么属于类本身,要么属于类的一个对象

3、永远不能独立执行方法,执行方法必须使用类或对象作为调用者。

5.2.2 方法的参数传递机制

Java方法的参数传递方式只有一种,值传递。 //将实际参数值的副本传入方法内,参数本身不受影响

引用类型传递仍然是值传递,但传递的是引用的副本,通过操作副本来访问对象也会改变对象本身。

(因为对象就是在堆内存里的那一个对象,并没有复制另一个对象,复制的是引用变量)


5.2.3 形参个数可变的方法

定义方法时,在最后一个形参的类型后面加三点…,表明该形参可以接收多个参数值。多个参数值被当成数组传入。

public class Varargs

{

   public static void test(int a, String… books)

  {

   for(String tmp : books)

{

  System.out.println(tmp);

}

System.out.println(a);

}

}
 

5.2.4 递归方法

方法调用自身,构成递归。

 

5.2.5 方法重载

Java允许一个类里里定义多个同名方法,只要形参列表不同就行。

如果一个类包含多个同名方法,则称为方法重载。

5.3 成员变量和局部变量

5.3.1 成员变量和局部变量

成员变量值在类里定义的变量,局部变量是在方法里定义的变量。

 

 

成员变量分为类变量和实例变量两种,定义成员变量时没有static修饰时就是实例变量,有static修饰就是类变量。

类变量在该类的准备阶段起就存在,直到系统完全销毁这个类,类变量的作用域和类的生存范围相同。

而实例变量则时从实例创建起存在,直到实例被销毁。

@成员变量无需显式初始化。系统会默认初始化。

 

局部变量根据定义形式不同,可以被分成3种。

形参,方法局部变量,代码块局部变量。

局部变量除了形参之外,都必须显式初始化。

 

5.3.2 成员变量初始化和内存中的运行机制

系统加载类或创建类的实例时,自动为成员变量分配内存空间,指定初始值。

 

5.3.3 局部变量的初始化和内存中的运行机制

局部变量定义后,必须经过显式初始化才能使用,系统不会为局部变量执行初始化。

定义局部变量后,系统并未为这个变量分配内存空间,知道赋初始值时,才分配内存。

局部变量不属于类或实例,因此它总是保存在其方法的栈内存中。

5.3.4 变量的使用规则

应该使用成员变量的情形:

1、变量用于描述某个类或对象的属性,比如人的身高,体重,应该使用成员变量。

如果是类相关,比如人的眼睛数量,应该所有人的眼睛都是2个,则应该定义为类变量。

2、变量用来保存类或实例的状态信息。比如五子棋的棋盘数组。

3、变量需要在多个方法间共享。

如果可能,尽量缩小局部变量的范围,节约内存的使用。

 

5.4 隐藏和封装

5.4.1 理解封装

封装指将对象的状态信息隐藏在对象内部,不允许外界直接访问对象内部信息,而是通过类提供的方法来实现对内部信息的操作和访问。

 

5.4..2 使用访问控制符

Java提供3个访问控制符:private protected public,代表3个访问控制级别。

另外还有一个默认的级别。

 

private(当前类访问权限)

default(包访问权限)

protected(子类访问权限)

public(公共访问权限)

 

@如果一个Java源文件没有publc 类,则Java源文件文件名可以是任意的。如果有public类,则文件名必须和public修饰的类名相同

 

实例:(使用封装的)Person类

public class Person

{

    private String name;

    private int age;

    public void setName(String name){

        if(name.length()>6 || name.length()<2){

            System.out.println("人名不符合要求");

            return;

        }

        else{

            this.name = name;

        }

    }

 

    public String getName(){

        return this.name;

    }

 

    public void setAge(int age){

        if(age>100 || age<0){

            System.out.println("年龄不合法");

            return;

        }

        else{

            this.age = age;

        }

    }

    public int getAge(){

        return this.age;

    }

}
 

类外,只能使用setter和getter方法来访问Person的属性。

 

 

@关于控制访问符

1、类里的绝大部分成员变量都应该使用private修饰,只有一些static修饰的,类似全局变量的成员变量才考虑public。

此外,有些方法只用于辅助其他方法的实现,称为工具方法,也应该用private修饰。

2、如果这个类将作为其他类的父类,里面的方法希望子类重写,而不希望外界调用,应该用protected修饰这些方法。


3、希望暴露给其他类的应该用public。因此,类的构造器应该用public修饰,允许其他地方创建类的实例。

 

 

5.4.3 package、import和import static

package用来解决重名问题

Java允许将一组相关的类放在一个package下,构成逻辑上的类库单元。

如果希望将一个类放在指定的包下,应该在源程序的第一个非注释行写下:

package packageName;

则这个文件的类都属于这个包。其他人使用该包下的类时,应该使用包名加类名。

 

将Hello 放在lee包下:

package lee;

public class Hello

{

    public static void main(String[] args){

        System.out.println("Hello, world!");

    }

}

 

使用

javac -d . Hello.java 编译,则会出现一个lee文件夹,里面有Hello.class

 

进入lee文件夹,执行 java lee.Hello

 

同一个包下的类可以自由访问,无需添加包前缀。

import 语句:

为了避免繁琐的前缀,Java引入了import关键字

import package.subpackage,,,ClassName;

@使用*表示全部类

一旦import导入某些类,就可以省略包前缀。

 

静态导入:

导入指定类的某个静态成员变量、方法。

语法:

import static package.subpackage…ClassName.fieldName|methodName;

 

 

5.4.4 Java的常用包

Java核心类都放在Java包及其子包下,Java许多扩展类都放在javax包及其子包下。

 

 

 

5.5 深入构造器

5.5.1 使用构造器执行初始化

构造器最大的作用就是创建对象时执行初始化。

public class ConstructorTest

{

    public String name;

    public int count;

    public ConstructorTest(String name,int count){

        this.name = name;

        this.count = count;

    }

    public static void main(String[] args){

        ConstructorTest ct = new ConstructorTest("lee", 22);

    }

}
 

@一旦提供自定义的构造器,系统就不再提供默认的构造器

 

 

5.5.2 构造器重载

类似方法重载

import jdk.internal.jshell.tool.resources.l10n;

 

public class ConstructorOverload

{

    public String name;

    public int count;

    public ConstructorOverload(){}

    public ConstructorOverload(String name,int count){

        this.name = name;

        this.count= count;

    }

    public static void main(String[] args){

        ConstructorOverload  co1 = new ConstructorOverload();

        ConstructorOverload  co2 = new ConstructorOverload("java",111);

       

    }

}

如果包含多个构造器,一个构造器包含另一个构造器全部的执行体。则可以用this调用.

public class Apple

{

    public String name;

    public String color;

    public double weight;

    public APPle(){}

    public Apple(String name,String color){

        this.name = name;

        this.color = color;

    }

    public Apple(String name,String color,double weight){

        this(name,color);

        this.weight = weight;

    }

}
 

5.6 类的继承

Java的继承是单继承,每个子类只有一个直接父类。

5.6.1 继承的特点

继承通过extends 关键字实现, 被继承的称为父类,实现继承的称为子类。

父类和子类的关系,是一般和特殊的关系,例如水果和苹果的关系。苹果是一种特殊的水果。

继承语法:

修饰符 class SubClass  extends Supclass

{

}

extends 直译为扩展,可以体现出子类对父类的扩展。

而现在一般翻译为继承,体现子类获得父类的全部成员变量和方法。

@子类不会获得父类的构造器。

例:

public class Fruit

{

    public double weight;

    public void info(){

        System.out.println("我是一个水果,重"+weight+"g!");

    }

}

 

 

public class Apple extends Fruit

{

   public static void main(String[] args){

       Apple a = new Apple();

       a.weight = 56;

       a.info();

   }

}
 

5.6.2 重写父类的方法

大多数时候,子类以父类为基础,扩展新的成员。但有时候,子类要重写父类的方法。例:

public class Bird

{

    public void fly(){

        System.out.println("起飞...");

        }

}

 

//鸵鸟

public class Ostrich extends Bird

{

    public void fly(){

        System.out.println("只能在地上跑");

    }

}
 

子类鸵鸟重写(Override)了Bird的fly()方法,重写也称覆盖。

 

 

方法重写遵循“两同两小一大”规则:

两同即方法名相同,形参列表相同; 两小指子类方法返回值类型比父类方法返回值类型 更小或相等,子类方法抛出异常比父类方法抛出的异常类更小或相等;

一大值子类方法的访问权限应该比父类方法访问权限更大或相等。

 

当子类覆盖父类方法后,子类对象无法访问被覆盖的父类方法,但可以在子类方法中调用父类中被覆盖的方法。使用super或父类类名作为调用者来调用被覆盖的方法。

如果父类方法使用private修饰,则对子类隐藏。

 

5.6.3 super限定

要在子类中方法中调用被覆盖的父类方法,可使用super限定

public class Ostrich extends Bird

{

    public void fly(){

        System.out.println("只能在地上跑");

}

public void call(){

       super.fly();  //调用父类方法

}

}
 

super用于限定对象调用它从父类继承得到的实例变量或方法。

如果子类定义了和父类同名的实例变量,则会隐藏父类实例变量,通过super可以访问被隐藏的实例变量。

 

5.6.4 调用父类构造器

子类不会获得父类构造器,子类构造器里可以调用父类构造器的初始化代码,类似于一个构造器调用另一个重载的构造器。

在子类中调用父类父类构造器使用super调用。

class Base

{

    public double size;

    public String name;

    public Base(double size,String name){

        this.size = size;

        this.name = name;

    }

}

 

public class Sub extends Base

{

    public String color;

    public Sub(double size,String name, String color){

        super(size,name); //调用父类构造器

        this.color = color;

    }  

}
 

@不管是否使用suoer调用父类构造器,子类构造器总会调用父类构造器一次。

 

5.7 多态

5.7.1 多态性

编译时类型由声明该变量的类型决定,运行时类型由实际赋给变量的对象决定。

如果运行和编译时类型不一致,就会出现多态(Polymorphism)。

class BaseClass

{

    public int book = 6;

    public void base(){

        System.out.println("父类的普通方法");

    }

    public void test(){

        System.out.println("父类的被覆盖方法");

    }

}

public class SubClass extends BaseClass

{

    public String book = "java";

    public void test(){

        System.out.println("子类覆盖父类的方法");

    }

    public void sub(){

        System.out.println("子类的普通方法");

    }

    public static void main(String[] args){

        BaseClass p = new SubClass();

        p.base();

        p.test(); //

    }

}
 

@与方法不同,对象的实例变量不具有多态性。

 

5.7.2 引用变量的强制类型转换

如果要一个引用变量调用它运行时类型的方法,则需要强制类型转换成运行时类型。

强制类型转换语法:

(type) variable

 

强制类型转化并不都可行,因此在强制类型转换之前先通过 instanceof运算符判断是否可以转换:

if(objPri instanceof String){

 String str = (String) objPri;

}

5.7.3 instanceof 运算符

前面的操作数为一个引用类型变量, 后一个操作数通常是一个类。

用于判断前面的对象是否是后面的类。

@ 前面的操作数要和后面的类有继承关系,否则会引起编译错误。

5.8 继承和组合

 

5.8.1 使用继承的注意点

继承带来高度复用的同时,也带来一个严重的问题:继承破坏了父类的封装性,子类可以直接访问父类的内部信息。

为了确保父类良好的封装性,设计父类应该遵循以下规则:

1、尽量隐藏父类的内部数据,尽量将父类成员设置为private。

2、不要让子类可以随意访问,修改父类的方法。父类中的(仅仅辅助其他功能实现的)工具方法,应该使用private修饰。

如果父类中方法必须为外部使用,又不想子类重写,可以使用final修饰符。 如果希望子类重写,又不希望外部类访问,可以使用protected。

3、尽量不要在父类构造器调用将要在子类中重写的方法。

@final class xx  final修饰的类不能被继承

5.8.2 利用组合实现复用(has-a)

如,借助Arm类实现Person类。

 

 

5.9 初始化块

与构造器作用类似。

 

5.9.1 使用初始化块

初始化块是Java类中可以出现的第4种成员。一个类可以有多个初始化块,按先后顺序执行。

初始化块的语法:

[修饰符] {

//初始化块的代码

}

@修饰符可以是static,称为静态初始化块

 

当创建Java对象时,系统总是先调用类定义的初始化块。

 

 

5.9.2 初始化块和构造器


某种程度上,初始化块是构造器的补充,如果两个构造器都有共同的无需参数的初始化行为,则可以提取到初始化块中。

@实际上初始化块是一种假象,使用javac编译后,初始化块会被移动到每个构造器中。

5.9.3 静态初始化块

使用static修饰符的初始化块。静态初始化块是和类相关的,总是在类初始化阶段执行。

 

静态初始化块和声明静态成员变量时指定初始值 都是类的初始化代码,执行顺序和代码排列顺序相同。


相关文章
|
6天前
|
Java 开发者
Java 面向对象新视界:揭秘子类如何“继承”父类精华,再添“创新”之笔
【6月更文挑战第16天】在Java的面向对象世界,子类继承父类的特性,如`Circle`继承`Shape`,展示“is-a”关系。子类不仅保留父类的`color`和`display`方法,还添加了`radius`属性及定制的显示逻辑。这种继承与创新允许代码复用,增强灵活性和可扩展性,使得构建复杂系统变得更加高效和模块化。通过持续的继承与定制,开发者能构建出一系列独具特色的类,充分展现面向对象编程的力量。
|
1天前
|
存储 Java C语言
Java面向对象课程设计--类管理系统
Java面向对象课程设计--类管理系统
11 1
|
1天前
|
Java
Java面向对象特征(二)----- 继承
Java面向对象特征(二)----- 继承
Java面向对象特征(二)----- 继承
|
5天前
|
Java 数据安全/隐私保护 开发者
Java是一种完全支持面向对象编程的语言,其面向对象特性包括封装、继承、多态和抽象等
【6月更文挑战第18天】**面向对象编程(OOP)通过对象封装状态和行为,实现问题域的抽象。Java全面支持OOP,核心特性包括**: - **封装**:保护数据安全,隐藏内部细节。 - **继承**:子类继承父类属性和行为,促进代码重用。 - **多态**:一个接口多种实现,增强灵活性和扩展性。 - **抽象**:通过接口和抽象类抽离共性,简化复杂性。 **Java的OOP便于理解和解决复杂系统问题。**
18 3
|
1天前
|
Java
Java面向对象特征(一)----- 封装
Java面向对象特征(一)----- 封装
|
2天前
|
机器学习/深度学习 Java API
杨校老师课堂之Java面向对象知识点整理集锦3
杨校老师课堂之Java面向对象知识点整理集锦
8 0
|
2天前
|
Java API 数据安全/隐私保护
杨校老师课堂之Java面向对象知识点整理集锦2
杨校老师课堂之Java面向对象知识点整理集锦
12 0
|
2天前
|
Java 编译器 开发工具
杨校老师课堂之Java面向对象知识点整理集锦1
杨校老师课堂之Java面向对象知识点整理集锦
6 0
|
5天前
|
Java 开发者 C++
Java面向对象的终极挑战:抽象类与接口的深度解析!
【6月更文挑战第17天】在Java OOP中,抽象类和接口助力代码复用与扩展。抽象类不可实例化,提供通用框架,适合继承;接口包含纯抽象方法,支持多态与松耦合。选择抽象类用于继承已有方法和状态,接口则适用于不相关类共享行为。Java 8后接口能含默认方法,增加设计灵活性。抽象类与接口常结合使用,以实现最佳设计,如`Shape`抽象类实现`Drawable`和`Selectable`接口,展现两者协同优势。理解和熟练运用这对概念是提升代码质量的关键。
|
6天前
|
Java
Java 面向对象新篇章:子类如何“站在巨人肩膀上”,继承与创新并存!
【6月更文挑战第16天】Java 中的子类继承父类,实现代码复用和扩展。子类自动获得父类属性和方法,减少冗余,保证一致性。通过示例展示了`Circle`类如何继承`Shape`类并添加新特性。子类不仅能继承,还能创新,如`Circle`类增加计算面积方法。这种继承与创新结合,构成Java面向对象编程的核心,支持构建灵活、高效的软件系统。