Java 面向对象(基础)

学习面向对象内容的三条主线

Java类及类的成员:(重点)属性、方法、构造器;(熟悉)代码块、内部类

面向对象的特征:封装、继承、多态

其他关键字的使用:this、super、package、import、static、final、interface、abstract等


面向对象编程

 面向对象的程序设计思想( Object Oriented Programming),简称 OOP

  • 关注的焦点是 类 :在计算机程序设计过程中,参照现实中事物,将事物的属性特征、行为特征抽 象出来,用类来表示。
  • 典型的语言:Java、C#、C++、Python、Ruby和PHP等
  • 代码结构:以 类 为组织单位。每种事物都具备自己的 属性 和 行为/功能 。
  • 是一种“ 设计者思维 ”,适合解决复杂问题。代码扩展性强、可维护性高。 

Java语言的基本元素:类和对象 

类和对象概述

类(Class)和对象(Object)是面向对象的核心概念。

1、什么是类

类:具有相同特征的抽象描述,是抽象的、概念上的定义

2、什么是对象

对象:实际存在的该类事物的每个个体,是具体的,因此特被称为实例(instance)。

类的成员概述

面向对象程序设计的重点是类的设计

类的设计,其实就是类的成员的设计

Java中用类class来描述事物也是如此。类,是一组相关属性和行为的集合,这也是类最基本的两 个成员。

面向对象完成功能的三步骤

步骤1:类的定义

类的定义使用关键字:class。格式如下:

[修饰符] class 类名

{

        属性声明;

        方法声明;

}

步骤2:对象的创建

  •  创建对象,使用关键字:new 
  •  创建对象语法:

//方式1:给创建的对象命名

//把创建的对象用一个引用数据类型的变量保存起来,这样就可以反复使用这个对象了

类名 对象名 = new 类名();

//方式2:

new 类名()  //也称为匿名对象 

步骤3:对象调用属性或方法 

  • 对象是类的一个实例,必然具备该类事物的属性和行为(即方法)
  • 使用" 对象名.属性 " 或 " 对象名.方法 "的方式访问对象成员(包括属性和方法)

匿名对象 (anonymous object) 

我们也可以不定义对象的句柄,而直接调用这个对象的方法。这样的对象叫做匿名对象。

如:new Person().shout();

使用情况 如果一个对象只需要进行一次方法调用,那么就可以使用匿名对象。

我们经常将匿名对象作为实参传递给一个方法调用。

对象的内存解析


JVM内存结构划分

HotSpot Java虚拟机的架构图如下。其中我们主要关心的是运行时数据区部分(Runtime Data Area)。

其中: 堆(Heap) :

此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。这一 点在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。

栈(Stack) :是指虚拟机栈。虚拟机栈用于存储局部变量等。局部变量表存放了编译期可知长度的各 种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类 型,它不等同于对象本身,是对象在堆内存的首地址)。 方法执行完,自动释放。

方法区(Method Area) :用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的 代码等数据。

对象名储存的是对象地址

直接打印对象名和数组名都是显示“类型@对象的hashCode值",所以说类、数组都是引用数据类型,引用数据类型的变量中存储的是对象的地址,或者说指向堆中对象的首地址。 

类的成员之一:成员变量(field)


如何声明成员变量 

  • 语法格式

[修饰符1] class 类名{

        [修饰符2] 数据类型 成员变量名 [= 初始化值];

说明:

        位置要求:必须在类中,方法外

        修饰符

                常用的权限修饰符有:private、缺省、protected、public

                其他修饰符:static、final

        数据类型

                任何基本数据类型(如int、Boolean) 或 任何引用数据类型。

        成员变量名

                属于标识符,符合命名规则和规范即可。

        初始化值

                 根据情况,可以显式赋值;也可以不赋值,使用默认值 

成员变量 vs 局部变量 

1、变量的分类:成员变量与局部变量

  • 变量的分类:成员变量与局部变量 在方法体外,类体内声明的变量称为成员变量。
  • 在方法体内部等位置声明的变量称为局部变量。

成员变量 与 局部变量 的对比 

相同点

  • 变量声明的格式相同: 数据类型 变量名 = 初始化值
  • 变量必须先声明、后初始化、再使用。
  • 变量都有其对应的作用域。只在其作用域内是有效的

不同点

  1. 声明位置和方式 (1)实例变量:在类中方法外 (2)局部变量:在方法体{}中或方法的形参列表、代 码块中
  2. 在内存中存储的位置不同 (1)实例变量:堆 (2)局部变量:栈
  3. 生命周期 (1)实例变量:和对象的生命周期一样,随着对象的创建而存在,随着对象被GC回收而消 亡, 而且每一个对象的实例变量是独立的。 (2)局部变量:和方法调用的生命周期一样,每一次方法 被调用而在存在,随着方法执行的结束而消亡, 而且每一次方法调用都是独立。
  4. 作用域 (1)实例变量:通过对象就可以使用,本类中直接调用,其他类中“对象.实例变量” (2)局 部变量:出了作用域就不能使用
  5. 修饰符(1)实例变量:public,protected,private,final,volatile,transient等 (2)局部变 量:final
  6. 默认值 (1)实例变量:有默认值 (2)局部变量:没有,必须手动初始化。其中的形参比较特殊, 靠实参给它初始化。 

对象属性的默认初始化赋值

当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值

类的成员之二:方法(method) 


方法(method、函数)的理解

  1. 方法 是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为 函数 或 过程 。
  2. 将功能封装为方法的目的是,可以 实现代码重用,减少冗余,简化代码
  3. Java里的方法 不能独立存在 ,所有的方法必须定义在类里。

举例:

  • Math.random()的random()方法 Math.sqrt(x)的sqrt(x)方法
  • System.out.println(x)的println(x)方法
  • new Scanner(System.in).nextInt()的nextInt()方法
  • Arrays类中的binarySearch()方法、sort()方法、equals()方法

如何声明方法 

1、声明方法的语法格式

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

        方法体的功能代码

}

(1)一个完整的方法 = 方法头 + 方法体。

  • 方法头就是 [修饰符] 返回值类型 方法名([形参列表])[throws 异常列表] ,也称为 方法签名 。通 常调用方法时只需要关注方法头就可以,从方法头可以看出这个方法的功能和调用格式。
  • 方法体就是方法被调用后要执行的代码。对于调用者来说,不了解方法体如何实现的,并不影响方 法的使用。 

方法头可能包含五个部分:修饰符,返回值类型,方法名,形参列表,throws异常列表 

方法体:方法体必须有{}括起来,在{}中编写完成方法功能的代码 

关于方法体中return语句的说明:

  • return语句的作用是结束方法的执行,并将方法的结果返回去
  • 如果返回值类型不是void,方法体中必须保证一定有 return 返回值; 语句,并且要求该返回值结果的 类型与声明的返回值类型一致或兼容。
  • 如果返回值类型为void时,方法体中可以没有return语句,如果要用return语句提前结束方法的执 行,那么return后面不能跟返回值,直接写return ; 就可以。
  • return语句后面就不能再写其他代码了,否则会报错:Unreachable code

补充:方法的分类:按照是否有形参及返回值 

如何调用实例方法 

方法通过方法名被调用,且只有被调用才会执行。

1、方法调用语法格式

对象.方法名([实参列表]) 

调用实例方法的注意点

  1. 必须先声明后使用,且方法必须定义在类的内部 
  2. 调用一次就执行一次,不调用不执行。
  3. 方法中可以调用类中的方法或属性,不可以在方法内部定义方法。 

类{

        方法1(){

        }

        方法2(){

        }

 关键字return的使用

return在方法中的作用:

        作用1:结束一个方法

        作用2:结束一个方法的同时,可以返回数据给方法的调用者

注意点:在return关键字的直接后面不能声明执行语句

方法调用内存分析

  • 方法 没有被调用 的时候,都在 方法区 中的字节码文件(.class)中存储。
  • 方法 被调用 的时候,需要进入到 栈内存 中运行。方法每调用一次就会在栈中有一个 入栈 动作,即 给当前方法开辟一块独立的内存区域,用于存储当前方法的局部变量的值。
  • 当方法执行结束后,会释放该内存,称为 出栈 ,如果方法有返回值,就会把结果返回调用处,如 果没有返回值,就直接结束,回到调用处继续执行下一条指令。
  • 栈结构:先进后出,后进先出。

方法的重载 (overload)


概念及特点

方法重载:在同一个类中,允许存在一个以上的同名方法,只要它们的参数列表不同即可。

                  参数列表不同,意味着参数个数或参数类型的不同(两同一不同)

重载的特点:与修饰符、返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参 数类型)。调用时,根据方法参数列表的不同来区别。

重载方法调用:JVM通过方法的参数列表,调用匹配的方法。

        先找个数、类型最匹配的

        再找个数和类型可以兼容的,如果同时多个方法可以兼容将会报错

可变参数的形参

在JDK 5.0 中提供了Varargs(variable number of arguments)机制。即当定义一个方法时,形参的类型可以 确定,但是形参的个数不确定,那么可以考虑使用可变个数的形参。

格式: 

方法名(参数的类型名 ...参数名) 

 

特点:

  1. 可变参数:方法参数部分指定类型的参数个数是可变多个:0个,1个或多个
  2. 可变个数形参的方法与同名的方法之间,彼此构成重载
  3. 可变参数方法的使用与方法参数部分使用数组是一致的,二者不能同时声明,否则报错。
  4. 方法的参数部分有可变形参,需要放在形参声明的最后
  5. 在一个方法的形参中,最多只能声明一个可变个数的形参 

方法的参数传递机制 

形参和实参

        形参(formal parameter):在定义方法时,方法名后面括号()中声明的变量称为形式参数,简称形参。

        实参(actual parameter):在调用方法时,方法名后面括号()中的使用的值/变量/表达式称为实际 参数,简称实参。

参数传递机制:

Java里方法的参数传递方式只有一种: 值传递 。 即将实际参数值的副本(复制品)传入方法内,而参数 本身不受影响。 

  • 形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
  • 形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参

递归(recursion)方法 

递归方法调用:方法自己调用自己的现象就称为递归。

递归的分类:直接递归、间接递归。

直接递归:方法自身调用自己。

public void methodA(){

        methodA();

}

间接递归:可以理解为A()方法调用B()方法,B()方法调用C()方法,C()方法调用A()方法。 

public static void A(){

        B();

}

public static void B(){

        C();

}

public static void C(){

        A();

说明:

  • 递归方法包含了一种 隐式的循环 。
  • 递归方法会 重复执行 某段代码,但这种重复执行无须循环控制。
  • 递归一定要向 已知方向 递归,否则这种递归就变成了无穷递归,停不下来,类似于 死循环 。最终 发生 栈内存溢出 

1. 递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环 慢的 多 ,所以在使用递归时要慎重。

2. 在要求高性能的情况下尽量避免使用递归,递归调用既花时间又 耗内存 。考虑使用循环迭 代 

对象数组 


数组的元素可以是基本数据类型,也可以是引用数据类型。当元素是引用类型中的类时,我们称为对象 数组。

注意点

对象数组,首先要创建数组对象本身,即确定数组的长度,然后再创建每一个元素对象,如果不创建, 数组的元素的默认值就是 null ,所以很容易出现 空指针异常NullPointerException 。

关键字:package、import


package(包)

package,称为包,用于指明该文件中定义的类、接口等结构所在的包。

语法格式

package 顶层包名.子包名 ;

说明:

一个源文件只能有一个声明包的package语句

package语句作为Java源文件的第一条语句出现。若缺省该语句,则指定为无名包。

包名,属于标识符,满足标识符命名的规则和规范(全部小写)、见名知意

        包通常使用所在公司域名的倒置:com.atguigu.xxx。

        大家取包名时不要使用" java.xx "包

包对应于文件系统的目录,package语句中用 “.” 来指明包(目录)的层次,每.一次就表示一层文件目录。

同一个包下可以声明多个结构(类、接口),但是不能定义同名的结构(类、接口)。不同的包下 可以定义同名的结构(类、接口) 

包的作用 

  • 包可以包含类和子包,划分 项目层次 ,便于管理
  • 帮助 管理大型软件 系统:将功能相近的类划分到同一个包中。比如:MVC的设计模式
  • 解决 类命名冲突 的问题
  • 控制 访问权限

import(导入) 

为了使用定义在其它包中的Java类,需用import语句来显式引入指定包下所需要的类。相当于 import语 句告诉编译器到哪里去寻找这个类 。

语法格式

import 包名.类名;

注意事项

  • import语句,声明在包的声明和类的声明之间。
  • 如果需要导入多个类或接口,那么就并列显式多个import语句即可
  • 如果使用 a.* 导入结构,表示可以导入a包下的所有的结构。举例:可以使用java.util.*的方式,一 次性导入util包下所有的类或接口。
  • 如果导入的类或接口是java.lang包下的,或者是当前包下的,则可以省略此import语句。
  • 如果已经导入java.a包下的类,那么如果需要使用a包的子包下的类的话,仍然需要导入。
  • 如果在代码中使用不同包下的同名的类,那么就需要使用类的全类名的方式指明调用的是哪个类。 
  • import static 组合的使用:调用指定类或接口下的静态的属性或方法

面向对象特征一:封装性(encapsulation) 

为什么需要封装?

随着程序越来越复杂,类会越来越多,那么类之间的访问边界必须把握好,面向对象的开发原则要 遵循“ 高内聚、低耦合 ”。

那什么又是“高内聚,低耦合”?

高内聚、低耦合是软件工程中的概念,也是UNIX 操作系统设计的经典原则。

内聚,指一个模块内各个元素彼此结合的紧密程度;耦合指一个软件结构内不同模块之间互连程度 的度量。内聚意味着重用和独立,耦合意味着多米诺效应牵一发动全身。

  • 高内聚 :类的内部数据操作细节自己完成,不允许外部干涉;
  • 低耦合 :仅暴露少量的方法给外部使用,尽量方便外部调用。 

何为封装性?

所谓封装,就是把客观事物封装成抽象概念的类,并且类可以把自己的数据和方法只向可信的类或者对 象开放,向没必要开放的类或者对象隐藏信息。

通俗的讲,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。

Java如何实现数据封装 

  • 实现封装就是控制类或成员的可见性范围。这就需要依赖访问控制修饰符,也称为权限修饰符来控 制。
  • 权限修饰符: public 、 protected 、 缺省 、 private 。具体访问范围如下: 

封装性的体现

成员变量/属性私有化

概述:私有化类的成员变量,提供公共的get和set方法,对外暴露获取和修改属性的功能。

实现步骤:

① 使用 private 修饰成员变量

private 数据类型 变量名 ; 

 代码如下:

public class Person{
    private String name;
    private int age;
    private boolean marry;
}

 ② 提供 getXxx 方法 / setXxx 方法,可以访问成员变量,代码如下:

public class Person{
    private String name;
    private int age;
    private boolean marry;

    public void setName(String n){
        name = n;
    }

    public String getName(){
        return name;
    }
    
    public void set Age(int a){
        age = a;
    }

    public int getAge(){
        return age;
    }

    public void setMarry(boolean m){
        marry = m;
    }

    public boolean isMarry(){
        return marry;
    }
}

 ③ 测试:

public class PersonTest{
    public static void main(String[] args){
        Person p = new Person();

        //实例变量私有化,跨类是无法直接使用的
        /*p.name = "张三";
        p.age = 23;
        p.marry = true;*/

        p.setName("张三");
        System.out.println("p.name = "+p.getName());
        

成员变量封装的好处:

让使用者只能通过事先预定的方法来 访问数据 ,从而可以在该方法里面加入控制逻辑,限制对成员 变量的不合理访问。还可以进行数据检查,从而有利于保证对象信息的完整性。

便于修改 ,提高代码的可维护性。主要说的是隐藏的部分,在内部修改了,如果其对外可以的访问 方式不变的话,外部根本感觉不到它的修改。例如:Java8->Java9,String从char[]转为byte[]内部实 现,而对外的方法不变,我们使用者根本感觉不到它内部的修改。

私有化方法


public class ArrayUtil{
    public int max(int[] arr){
        //求int型数组的最大值
        int maxValue = arr[0];
        for(int i = 1;i<arr.length;i++){
            if(maxValue < arr[i]){
                maxValue = arr[i];
            }
        }
        return maxValue;
    }
    public int sum(int[] arr){
        int sum = 0;
        //求数组元素的总和
        for(int i = 0;i<arr.length;i++){
            sum += arr[i];
        }
        return sum;
    }

    public int avg(int[] arr){
        //求int类型数组的元素平均值
        int sumValue = sum(arr);
        return sumValue / arr.length;
    }
    //创建一些重载上述方法
    //public double max(double[] arr){}
    //public float max(float[] arr){}
    //public byte max(byte[] arr){}
    public void print(int[] arr){
        //遍历数组
        for(int i = 0;i < arr.length;i++){
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
    public int[] copy(int[] arr){
        //复制数组
        int[] arr1 = new int[arr.length];
        for(int i = 0;i < arr.length;i++){
            arr1[i] = arr[i];
        }
        return arr1;
    }
    public void reverse(int[] arr){
        //反转数组
        for(int i = 0,j = arr.length - 1;i < j;i++,j--){
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    public void sort(int[] arr,String desc) {

        if ("ascend".equals(desc)) {//if(desc.equals("ascend")){
            for (int i = 0; i < arr.length - 1; i++) {
                for (int j = 0; j < arr.length - 1 - i; j++) {
                    if (arr[j] > arr[j + 1]) {
//                        int temp = arr[1];
//                        arr[j] = arr[j + 1];
//                        arr[j + 1] = temp;
                        swap(arr, j, j + 1);
                    }
                }
            }
        }else{
            System.out.println("您输入的排列方式有误!");
        }
    }

    private void swap(int[] arr,int i,int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    public int getValue(int[] arr,int value){
        //线性查找 指定的value值在arr数组中出现的位置
        for(int i = 0;i < arr.length;i++){
            if(value == arr[i]){
                return i;
            }
        }
        return -1;
    }
}

注意: 开发中,一般成员实例变量都习惯使用private修饰,再提供相应的public权限的get/set方法访问。 对于final的实例变量,不提供set()方法。(后面final关键字的时候讲) 对于static final的成员变量,习惯上使用public修饰。

类的成员之三:构造器(Constructor)


 我们new完对象时,所有成员变量都是默认值,如果我们需要赋别的值,需要挨个为它们再赋值,太麻烦了。我们能不能在new对象时,直接为当前对象的某个或所有成员变量直接赋值呢?

可以,Java给我们提供了 构造器(Constructor) ,也称为 构造方法 。

构造器的作用

new对象,并在new对象的时候为实例变量赋值

举例:Person p = new Person("Perter",15);

解释:如同我们规定每个“人”一出生就必须先洗澡,我们就可以在“人”的构造器中加入完成“洗澡”的程序 代码,于是每个“人”一出生就会自动完成“洗澡”,程序就不必再在每个人刚出生时一个一个地告诉他们 要“洗澡”了。

构造器的语法格式

[修饰符] class 类名{

        [修饰符] 构造器名(){

                // 实例初始化代码

        }

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

                // 实例初始化代码

        }

}

说明:

        1.构造名必须与它所在的类名相同。 

        2.它没有返回值,所以不需要返回值类型,也不需要void。

        3.构造器的修饰符只能是权限修饰符,不能被其他任何修饰。比如,不能被static、final、

           synchronized、abstract、native修饰,不呢能有return语句返回值

代码如下:

public class Student{
    private String name;
    private int age;

    //无参构造
    public Student() {}

    //有参构造
    public Student(String n,int a){
        name = n;
        age = a;
    }

    public String getName(){
        return name;
    }
    public void setName(String n){
        name = n;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int a){
        age = a;
    }
    public String getInfo(){
        return"姓名"+name+",年龄"+age;
    }
}
public class TestStudent{
    public static void main(String[] args){
        //调用无参构造创建学生对象
        Student s = new Student();

        //调用有参构造创建学生对象
        Student s2 = new Student("张三",23);

        System.out.println(s1.getInfo());
        System.out.println(s2.getInfo());
    }
}    

 使用说明

1. 当我们没有显式的声明类中的构造器时,系统会默认提供一个无参的构造器并且该构造器的修饰 符默认与类的修饰符相同

2. 当我们显式的定义类的构造器以后,系统就不再提供默认的无参的构造器了。

3. 在类中,至少会存在一个构造器。

4. 构造器是可以重载的。

阶段性知识补充


类中属性赋值过程

1、在类的属性中,可以有哪些位置给属性赋值?

① 默认初始化 ② 显式初始化 ③ 构造器中初始化 ④ 通过"对象.属性"或"对象.方法"的方式,给属性赋值

2、这些位置执行的先后顺序是怎样?

顺序:① - ② - ③ - ④

3、说明:

  • 上述中的①、②、③在对象创建过程中,只执行一次。
  • ④ 是在对象创建后执行的,可以根据需求多次执行。

JavaBean

JavaBean是一种Java语言写成的可重用组件。 好比你做了一个扳手,这个扳手会在很多地方被拿去用。这个扳手也提供多种功能(你可以拿 这个扳手扳、锤、撬等等),而这个扳手就是一个组件。 所谓JavaBean,是指符合如下标准的Java类:

  • 类是公共的
  • 有一个无参的公共的构造器
  • 有属性,且有对应的get、set方法

用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创造的对象进行 打包,并且其他的开发者可以通过内部的JSP页面、Servlet、其他JavaBean、applet程序或者应用来 使用这些对象。用户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变。

《Think in Java》中提到,JavaBean最初是为Java GUI的可视化编程实现的。你拖动IDE构建工具创 建一个GUI 组件(如多选框),其实是工具给你创建Java类,并提供将类的属性暴露出来给你修改 调整,将事件监听器暴露出来。

示例

public class JavaBean {
    private String name; // 属性一般定义为private
    private int age;
    public JavaBean() {
    }
    public int getAge() {
        return age;
    }
    public void setAge(int a) {
        age = a;
    }
    public String getName() {
        return name;
    }
    public void setName(String n) {
        name = n;
    }
}

 UML类图

  • UML(Unified Modeling Language,统一建模语言),用来描述 软件模型 和 架构 的图形化语言。
  • 常用的UML工具软件有 PowerDesinger 、 Rose 和 Enterprise Architect 。
  • UML工具软件不仅可以绘制软件开发中所需的各种图表,还可以生成对应的源代码。
  • 在软件开发中,使用 UML类图 可以更加直观地描述类内部结构(类的属性和操作)以及类之间的关 系(如关联、依赖、聚合等)。

        +表示 public 类型, - 表示 private 类型,#表示protected类型

        方法的写法: 方法的类型(+、-) 方法名(参数名: 参数类型):返回值类型

        斜体表示抽象方法或类。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值