Administrator
发布于 2023-01-17 / 7 阅读
0
0

java入门(六)——面向对象

面向过程&面向对象

面向过程(POP):把事情拆分成几个步骤(相当于拆分成一个个的方法和数据),然后按照一定的顺序执行,是一种线性思维模式。

面向对象(OOP):面向对象会把事物抽象成对象的概念,先抽象出对象,然后给对象赋一些属性和方法,然后让每个对象去执行自己的方法,是一种分类思维模式。比如车间工人分工干活,就是一种面向对象的思想。

面向过程:

优点:效率高,因为不需要实例化对象。

缺点:耦合度高,扩展性差,不易维护(例如:每个步骤都要有,不然就不行)

面向对象:

优点:耦合低(易复用),扩展性强,易维护,由于面向对象有封装、继承、多态性的特点,可以设计出低耦合的系统,使系统更加灵活、更加易于维护。

缺点:效率比面向过程低。

对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。

面向对象

什么是面向对象

Java的编程语言是面向对象的,采用这种语言进行编程称为面向对象编程(Object-Oriented Programming, OOP)。

面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据。

面向对象3大特征

  1. 封装性:封装是一种信息隐蔽技术,通过类的说明实现封装,即将程序中数据和基于数据的操作方法封装起来,使之成为一个整体,其载体就是类,它是程序中的最小模块。这样可以保护数据和操作方法的安全,是对象的重要特性。
  2. 继承性:主要描述的是类与类之间的关系,通过继承,可以在无需重新编写原有类的情况下,对原有类的功能进行扩展。
  3. 多态性:一般类中定义的属性或方法,被子类继承之后,可具有不同的数据类型或不同的行为,这样,一个程序可以存在同名的不同方法,不同类的对象可以响应同名的方法,具体的实现方法却不同。多态性使得语言具有灵活性、抽象、行为共享和代码共享的优势,很好地解决了应用程序方法同名问题。

类与对象

类与对象的定义

  • 类(Class)是对一类事物的描述,是抽象的、概念上的定义,大部分类都会拥有属性和方法。比如学生。
  • 对象是实际存在的该类事物的每个个体,因而也称为实例(instance),比如小明。

对Java语言来说,一切皆是对象。把现实世界中的对象抽象地体现在编程世界中,一个对象代表了某个具体的操作。一个个对象最终组成了完整的程序设计,这些对象可以是独立存在的,也可以是从别的对象继承过来的。对象之间通过相互作用传递信息,实现程序开发。

使用类类型、数组类型、接口类型声明出的变量,都可以指向对象,这种变量就是引用类型变量,简称引用。

方法

Java方法是语句的集合,是解决一类问题的步骤的有序组合,属于类的成员。

方法的优点

  • 1. 使程序变得更简短而清晰。
  • 2. 有利于程序维护。
  • 3. 可以提高程序开发的效率。
  • 4. 提高了代码的重用性。

方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:sayHello。

方法的定义

一般情况下,定义一个方法包含以下语法:

修饰符 返回值类型 方法名(参数列表)异常抛出类型{
    ... 
    方法体 
    ... 
    return 返回值; //如果有返回值的话
}

下面是一个方法的组成部分:

  • 修饰符:修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。publicstaticabstractfinal等等都是修饰符,一个方法可以有多个修饰符。如果一个方法或者属性有多个修饰符,这多个修饰符是没有先后顺序的。
  • 返回值类型 :方法可能会返回值,返回指定数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,无需return,返回类型使用关键字void
  • 方法名:是方法的实际名称。方法名和参数列表共同构成方法签名。
  • 参数列表:参数像是一个占位符,当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
  • 异常抛出类型:如果方法中的代码在执行过程中,可能会出现一些异常情况那么就可以在方法上把这些异常声明并抛出,也可以同时声明抛出多个异常,使用逗号隔开即可。
  • 方法体:方法体包含具体的语句,定义该方法的功能。

实例

public void sayHello(){
    System.out.println("hello");
}

public int max(int x,int y){
    return x > y ? x : y;
}

public static String getMsg(String msg){
    return "收到了消息:" + msg;
}

方法调用

在类中定义了方法,这个方法中的代码并不会执行,当这个方法被调用的时候,方法中的代码才会被一行一行顺序执行。

方法分为以下静态方法和非静态方法:

  • 非静态方法:没有使用static修饰符修饰的方法就是非静态方法.。调用这种方法的时候,是需要使用对象的。因为非静态方法是属于对象的。(非静态属性也是一样的)
  • 静态方法:使用static修饰符修饰的方法就是静态方法调用这种方法的时候,可以使用对象调用,也可以使用类来调用但是推荐使用类进行调用因为静态方法是属于类的。(静态属性也是一样的)

实例

public class TestOne {
    public static void main(String[] args) {
        sleep("小明");//静态方法调用
        TestOne testOne = new TestOne();
        int minNum = testOne.min(5,3);//非静态方法调用
        System.out.println(minNum);//输出结果
    }

    public static void sleep(String name) {
        System.out.println(name + "正在睡觉");
    }

    public int min(int x, int y) {
        return x < y ? x : y;
    }
}

输出结果

小明正在睡觉
3

方法之间的调用

类中方法中的调用,两个方法都是静态或者非静态都可以互相调用,当一个方法是静态,一个方法是非静态的时候,非静态方法可以调用静态方法,反之不能。同样的,在同一个类中,静态方法内不能直接访问到类中的非静态属性。

方法传参

形参和实参:参数列表中的参数是形参,调用方法实际传递进来的是实参。

调用方法进行传参时,分为值传递和引用传递两种。

  • 值传递是实参把自己变量本身存的简单数值赋值给形参,使用的是基本数据类型。
  • 引用传递是实参把自己变量本身存的对象内存地址值赋值给形参,使用的是引用数据类型。

变量作用域

变量作用域,即变量可被访问的范围。Java中变量分为全局变量局部变量。

全局变量即定义在方法/代码块之外的变量为全局变量,它的作用域是全局作用域,即整个源文件。局部变量即定义在方法体内或代码块内的变量称为局部变量,它的作用域为局部作用域,即:仅在定义这个变量的方法体或代码块内可访问。

在同一作用域范围的包裹下成员变量名和局部变量名是可以变量名相同的,在同一个作用域范围的包裹下局部变量和局部变量不可以变量名相同(作用域内不能重复命名),在方法中使用变量的时候如果不指明使用成员变量还是局部变量,那么默认的就是使用局部的那个变量,如果当前方法/代码块中不存在该变量,则会去寻找有没有这个名称的全局变量,如果有访问,如果没有报错。

当局部变量和成员变量出现了重名问题,Java会根据就近原则,优先使用局部变量,如果需要使用成员变量的话,可以使用this关键字进行区分,this代表的是当前对象。

方法重载

类中有多个方法,有着相同的方法名,但是方法的参数各不相同,这种情况被称为方法的重载。方法的重载可以提供方法调用的灵活性。

方法重载必须满足以下条件

  1. 方法名必须相同
  2. 参数列表必须不同(参数的类型、个数、顺序的不同)
  3. 方法的返回值可以不同,也可以相同。

java,判断一个类中的俩个方法是否相同,主要参考俩个方面:方法名字和参数列表

public class Test {
    /**
     * 1、2、3、4、5方法属于重载  第6个方法和第1个方法方法名和参数列表相同  会报错
     */

    //第1个方法
    public void print(String a) {
        System.out.println(a);
    }

    //第2个方法
    public void print(int a) {
        System.out.println(a);
    }
    //第3个方法
    public double print(double d) {
        return d;
    }

    //第4个方法
    public void print(String a, int b) {
        System.out.println(a + b);
    }

    //第5个方法
    public void print(int b, String a) {
        System.out.println(a + b);
    }

    //第6个方法
//    public String print(String a) {
//        return a;
//    }
}

可变参数

JDK 1.5 开始,Java支持传递同类型的可变参数给一个方法。

方法的可变参数的声明如下所示:

typeName... parameterName

在方法声明中,在指定参数类型后加一个省略号(...) 。

一个方法中只能指定一个可变参数,它必须是方法的最后一个参数。任何普通的参数必须在它之前声明。

实例

public class TestOne {
    public static void main(String[] args) {
        print(3, 7);
        System.out.println("-------------");
        print(5, 2, 11, 36);
    }

    public static void print(int... nums) {
        for (int num : nums) {
            System.out.println(num);
        }
    }
}

以上实例编译运行结果如下:

3
7
-------------
5
2
11
36

递归

递归是一种常见的解决问题的方法,即把问题逐渐简单化。递归的基本思想就是自己调用自己,一个使用递归技术的方法将会直接或者间接的调用自己。

利用递归可以用简单的程序来解决一些复杂的问题。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计 算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。

递归三要素:

  1. 明确递归终止条件;
  2. 给出递归终止时的处理办法;
  3. 提取重复的逻辑,缩小问题的规模。

代码示例

public class Test1 {
    //5*4*3*2*1
    public static void main(String[] args) {
        System.out.println(f(5));
    }

    public static int f(int n) {
        if (n == 1) {
            return 1;
        }
        else {
            return n*f(n - 1);
        }
    }
}

输出

120

创建对象

new创建

使用new关键字创建的时候,除了分配内存空间之外,还会给 创建好的对象 进行默认的初始化 以 及对类中构造器的调用。

  1. 为对象分配内存空间,将对象的实例变量自动初始化默认值(实例变量的隐式赋)
  2. 如果代码中实例变量有显式赋值,那么就将之前的默认值覆盖掉。例如:显式赋值 private int age= 18; 这段会覆盖掉默认值0。
  3. 调用构造器
  4. 把对象内存地址值赋值给变量。(=号赋值操作)

构造器

类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下俩个特点

  1. 必须和类的名字相同
  2. 必须没有返回类型,也不能写void

构造器的作用

  1. 使用new创建对象的时候必须使用类的构造器
  2. 构造器中的代码执行后,可以给对象中的属性初始化赋值

构造器相关语法规则

  1. 分为无参构造器、有参构造器、默认构造器,每个类都至少有一个构造器
  2. 默认构造器的修饰符与所属类的修饰符一致
  3. 一旦显式定义了构造器,则系统不再提供默认构造器
  4. 一个类可以创建多个重载的构造器(无参、有参)

对象实例化过程

  1. 类加载,初始化类中的静态属性
  2. 执行静态代码块
  3. 分配内存空间,同时初始化非静态的属性(赋默认值,0/false/null)
  4. 调用父类构造器
  5. 中的属性进行显示赋值(如果有的话)
  6. 执行匿名代码块
  7. 执行构造器
  8. 返回内存地址

内存分析

java内存分为堆、栈、方法区。

stack

  1. 每个线程私有,不能实现线程间的共享!
  2. 局部变量放置于栈中。
  3. 栈是由系统自动分配,速度快!栈是一个连续的内存空间!

heap

  1. 放置new出来的对象!
  2. 堆是一个不连续的内存空间,分配灵活,速度慢!

方法区(也是堆) 

  1. 被所有线程共享!
  2.   用来存放程序中永远是不变或唯一的内容。(类代码信息、静态变量、字符串常量)

修饰符

访问修饰符

static修饰符

1static变量

在类中使用static修饰的成员变量就是静态变量反之为非静态变量。

静态变量和非静态变量的区别

静态变量属于类的"可以"使用类名来访问,非静态变量是属于对象的"必须"使用对象来访问。

静态变量对于类而言在内存中只有一个能被类的所有实例所共享。实例变量对于类的每个实例都有一份它们之间互不影响.

在加载类的过程中为静态变量分配内存实例变量在创建对象时分配内存。

2static方法

在类中,使用static修饰的成员方法,就是静态方法,反之为非静态方法。

静态方法和非静态方法的区别

静态方法"不可以"直接访问类中的非静态变量和非静态方法,但是"可以"直接访问类中的静态变量和静态方法。

非静态方法"可以"直接访问类中的非静态变量和非静态方法,"可以"直接访问类中的静态变量和静态方法。

注意:thissuper在类中属于非静态的变量.(静态方法中不能使用)

3、代码块和静态代码块

【类中可以编写代码块和静态代码块】

public class A {
    {
        //代码块(匿名代码块)
    }

    static{
        //静态代码块
    }
}

匿名代码块是在创建对象的时候自动执行的,并且在构造器执行之前。同时匿名代码块在每次创建对象的时候都会自动执行.

静态代码块是在类加载完成之后就自动执行,并且只执行一次.

:每个类在第一次被使用的时候就会被加载,并且一般只会加载一次。

执行顺序:静态代码块>匿名代码块>构造器

【匿名代码块和静态代码块的作用】

匿名代码块的作用是给对象的成员变量初始化赋值,但是因为构造器也能完成这项工作,所以匿名代码块使用的并不多。

静态代码块的作用是给类中的静态成员变量初始化赋值。

final修饰符

  1. 修饰类:不能被继承,没有子类
  2. 修饰方法:可以被继承,但无法被子类重写
  3. 修饰变量:只能赋值一次,相当于常量
  4. 修饰非静态成员变量:只有一次机会赋值,可以在声明的时候、匿名代码块或构造器中赋值
  5. 修饰静态成员变量:只有一次机会赋值,可以在声明的时候或静态代码块中赋值
  6. 修饰引用变量:可以修改引用变量的内部值,但不能修改指向地址

abstract修饰符

abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类。

抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。

抽象类,不能使用new关键字来创建对象,它是用来让子类继承的。

抽象方法,只有方法的声明,没有方法的实现(没有大括号及方法体),它是用来让子类实现的。

:子类继承抽象类后,需要实现抽象类中没有实现的抽象方法,否则这个子类也要声明为抽象类。

抽象类和抽象方法也是Java多态的一种实现形式。

接口

接口使用interface关键字创建,使用implements关键字实现接口。

  1. Java接口中的成员变量默认都是public,static,final类型的(都可省略),必须被显示初始化,即接口中的成员变量为常量。
  2. Java接口中的方法默认都是public,abstract类型的(都可省略),没有方法体,不能被实例化,但是使用default修饰的方法可以有方法体和默认实现,且无需被实现类重写。
  3. Java接口中只能包含public,static,final类型的成员变量和public,abstract类型的成员方法
  4. 接口中没有构造方法,不能被实例化
  5. 一个接口不能实现(implements)另一个接口,但它可以继承多个其它的接口
  6. Java接口必须通过类来实现它的抽象方法
  7. 当类实现了某个Java接口时,它必须实现接口中的所有抽象方法,否则这个类必须声明为抽象类
  8. 不允许创建接口的实例(实例化),但允许定义接口类型的引用变量,该引用变量引用实现了这个接口的类的实例
  9. 一个类只能继承一个直接的父类,但可以实现多个接口,间接的实现了多继承。

代码示例

//工作接口
public interface Work {
    public static final String WORK_TYPE = "IT";
    String WORK_LANGUAGE = "java";//默认使用public,static,final修饰  并且需要显示初始化
    //工作年限
    public abstract void workYear();
    //工作地点
    void workAddress();//默认使用public abstract修饰
}

//旅游接口
public interface Travel {
    void travelTo();

    //默认实现方法 加关键字default  无需重写
    default void buyTicket() {
        System.out.println("买票");
    }
}

//员工实现工作和旅游接口 必须重写所有方法,否则只能定义为抽象类abstract class
public class Staff implements Work,Travel {
    @Override
    public void workYear() {
        System.out.println("我已经工作了10年");
    }

    @Override
    public void workAddress() {
        System.out.println("我在北京工作");
    }

    @Override
    public void travelTo() {
        System.out.println("今年去丽江旅游");
    }

//    @Override
//    public void buyTicket() {
//        System.out.println("员工买票");
//    }

    public void hi(){
        System.out.println("hello");
    }

    public static void main(String[] args) {
        Work work = new Staff();//声明接口指向接口实现类
        System.out.println(Work.WORK_TYPE + "===" + Work.WORK_LANGUAGE);//通过接口或实现类都能调用成员变量
        work.workAddress();
        work.workYear();

        Travel travel = new Staff();//声明接口指向接口实现类
        travel.travelTo();
        travel.buyTicket();//如果Staff类重写了此方法  那么会调用Staff中的方法实现 输出 员工买票

        Staff staff = new Staff();//声明实现类可以调用实现类特有的属性和方法
        staff.hi();
        //staff.buyTicket();//输出 员工买票
    }
}

输出结果

IT===java
我在北京工作
我已经工作了10年
今年去丽江旅游
买票
hello

【注】接口在以后工作中是一个很重要的概念,广义上更是一种规范的思想。平常我们接触到的发送短信、地图信息等就是通过各式各样的Api接口进行对接调用的。以后框架的学习中会对接口有进一步的认识。

本章篇幅有点长,三大特性我们放在下章~~


评论