Java进阶-面向对象

Java继承

简单来说

继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类

就像生物一样,狮子和老虎都是动物,可以说狮子继承动物、老虎继承动物。所以继承需要符合的关系是:is-a,父类更通用,子类更具体

从面向对象的特性上说

继承是通过已存在的类作为基础创建的新类(新的功能,技术,也可以复用父类功能),但不能选择性的继承

总结

子类拥有父类所有属性和方法(包括私有,但无法访问,只是拥有)

子类可以对父类进行扩展

Java 是单继承(单继承:一个子类只能继承一个父类),但可多重继承,多重继承:C 类继承 B 类,B 类又继承 A 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类。这是 Java 继承区别于 C++ 继承的一个特性,在下文知识补充后,有详细继承笔记

知识补充:方法

基础笔记中提到,方法就是类的行为(功能),并且举了很多例子,告诉我们java提供的类(如StringBuffer)怎么用,方法的参数和返回结果是什么

java的方法按static关键字也可以区分为实例方法静态方法

实例方法

实例方法是定义在类中,而不是在类的静态部分(使用 static 修饰的部分)
实例方法依赖于类的实例,必须通过创建类的对象来调用
在方法定义中,不包含 static 关键字

静态方法(也称为类方法)

静态方法是定义在类的静态部分,使用 static 修饰的方法
静态方法不依赖于类的实例,可以通过类名直接调用,无需创建类的对象
在方法定义中,包含 static 关键字


这里将补充一些更抽象的名词:

构造方法

构造方法是一个特殊的方法,其名字必须和类相同且没有返回类型(补充:构造方法不能通过 abstract、static、final、synchronized 关键字修饰)

构造器(constructor,也叫构造函数),初始化对象(不是创建),是在对象被创建时被自动调用的特殊方法,作用一般是为了给属性赋值

因为如果构造方法的名字与类名不相同,JAVA的编译器就会将该方法归结为成员方法,而不会将其认定为构造方法,而构造方法是无返回值类型的,但是成员方法是必须有返回值类型,这就会导致编译通不过

默认构造方法

也就是无参构造方法:构造方法没有任何参数

无参构造方法是可省的,程序员并不需要显式的声明无参构造方法,更倾向于把这项工作交给编译器

默认构造方法的目的主要是为对象的字段提供默认值,或是在创建对象时需要将业务逻辑写到这个过程中。例:

public class father {
    public int age;
    public String name;
    public static void main(String[] args) {
        father father = new father();
        System.out.println("name:"+father.name+"\n"+"age:"+father.age);
    }
}

该类只有两个属性,并在主方法中实例化

输出的结果是:

name:null
age:0

name 是 String 类型,所以默认值为 null,age 是 int 类型,所以默认值为 0

有参构造方法

有参数的构造方法,可以为不同的对象提供不同/相同的值,例:

public class father {
    public int age;
    public String name;
    public father(String inputName,int inputAge){
        //方法名和类名相同的叫构造方法
        name = inputName;
        age = inputAge;
    }
    public static void main(String[] args) {
        father father1 = new father("我爸",40);
        father father2 = new father("你爸",40);
        System.out.println("我爸的名字是:"+father1.name+" "+"年龄是"+father1.age);
        System.out.println("你爸的名字是:"+father2.name+" "+"年龄是"+father2.age);
    }
}

输出的结果是

我爸的名字是:我爸 年龄是40
你爸的名字是:你爸 年龄是40

在上面的例子中,构造方法有两个参数(name 和 age),这样的话,我们在创建对象的时候就可以直接为 name 和 age 赋值了

补充

子类是不继承父类的构造器(构造方法或者构造函数)的,只是调用,所以不能重写父类构造器。如果父类是有参构造,子类必须通过super关键字调用父类的构造器以适配参数列表

关于有参构造器和无参构造器的结论:有参构造的作用是为了给对象中的属性,也就是成员变量赋值。而无参构造器的作用就是在创建对象过程中完成某些初始化操作

作用域与变量

作用域(Scope)指的是一个变量在程序中可被访问的范围。Java中有三种主要的作用域:

局部作用域(Local Scope):

变量在方法、代码块或构造方法中声明,只在这个范围内可见。
当该方法、代码块或构造方法执行结束时,局部变量的作用域也结束

public class ScopeExample {
    public void exampleMethod() {
        int localVar = 10; // 局部变量(注意这个变量)要赋值才能用
        System.out.println(localVar);
    }
    public static void main(String[] args) {
        ScopeExample example = new ScopeExample();
        example.exampleMethod();
        // System.out.println(localVar); // 这里会导致编译错误,因为 localVar 不在这个作用域内
    }
}
实例作用域(Instance Scope):

成员变量在类中声明,可以被类的所有实例访问
实例作用域的变量在对象创建后存在,并随着对象的生命周期而存在

public class ScopeExample {
    // 实例变量(成员变量也叫属性(注意这个变量))
    private int instanceVar;//属性可以不赋值,因为有默认值

    public void exampleMethod() {
        System.out.println(instanceVar); // 可以访问实例变量
    }
    public static void main(String[] args) {
        ScopeExample example = new ScopeExample();
        example.exampleMethod();
    }
}
类作用域(Class Scope):

静态变量在类中声明,可以被类的所有实例和类本身访问
类作用域的变量在程序运行期间存在,并且在类加载时初始化

public class ScopeExample {
    // 静态变量(注意这个变量)
    private static int classVar;//可以不赋值,因为有默认值

    public void exampleMethod() {
        System.out.println(classVar); // 可以访问静态变量
    }
    public static void main(String[] args) {
        ScopeExample example = new ScopeExample();
        example.exampleMethod();

        System.out.println(classVar); // 也可以直接通过类名访问静态变量
    }
}

结论:可以说类作用域和实例作用域之间的区别主要在于是否使用了 static 关键字。这个关键字决定了变量是类级别的还是对象级别的,而局部作用域的区别则是在哪里声明变量(类中和方法中声明的区别
变量补充:常量(Final Variables):
使用 final 关键字修饰的变量,它的值在声明后不能被修改。
成员常量可以是实例变量或静态变量。成员就等于类的,实例的而static是类的,和类中的实例(对象)无关,并且在加载类时就存在
关于常量:

public class Example {
    // 成员常量
    private final int constantVar = 42;
    public static final double PI = 3.14;
}

Super和this关键字

super关键字:通过super关键字来实现对父类成员的访问,用来引用当前对象的父类

this关键字:代表当前类对象的引用(地址) ,this.属性就是这个对象(当前对象)的属性

static和final关键字

static关键字

static 是关键字,用于修饰类的成员变量、方法、代码块等,它表示这些成员是类级别的,而不是实例级别的。也就是说,它们属于类本身,而不是类的实例。使用时,直接用类点出来就可以,不需要创建实例,下面是用法:

1.static 变量

在类中使用 static 修饰的成员变量称为静态变量,也叫类变量。静态变量与类相关联,而不是与类的每个实例相关联。因此,静态变量可以被类的所有实例共享。静态变量一般在定义时就赋值,也可以在静态代码块中进行初始化。

例如,下面的代码中定义了一个名为 count 的静态变量:

public class MyClass {
    static int count = 0;
}

2.static方法(前文提到过)

与静态变量类似,但注意:静态方法中不能访问非静态成员变量和方法,因为它们只能访问静态成员变量和方法。

例如,下面的代码中定义了一个名为 add 的静态方法:

public class MyClass {
    static int add(int a, int b) {
        return a + b;
    }
}

3.static代码块

在类中使用 static 修饰的代码块称为静态代码块,它在类被加载时自动执行。静态代码块中一般用于初始化静态变量。静态代码块中的语句只会被执行一次,且在类被加载时执行。

例如,下面的代码中定义了一个静态代码块来初始化静态变量 count

public class MyClass {
    static int count;
    static {
        count = 0;
    }
}

需要注意的是,static 关键字可以与访问修饰符(如 publicprivateprotected)一起使用,以控制静态成员的访问权限。同时,使用静态成员变量和方法时,应该遵循一些最佳实践,比如在多线程环境中,应该考虑静态变量的线程安全性

final关键字

在Java中,final 是一个关键字,用于修饰变量、方法和类,表示这些元素不可被修改或继承。下面是用法:

1.final 变量

在Java中,使用 final 修饰的变量称为常量,它的值在定义之后不能再被修改。常量一般使用大写字母来表示,并且在定义时必须进行初始化

例如,下面的代码中定义了一个名为 PI 的常量:

javaCopy code
final double PI = 3.1415926;

2.final 方法

在Java中,使用 final 修饰的方法称为最终方法,它不能被子类重写。最终方法在设计框架时比较有用,可以保证框架的稳定性

例如,下面的代码中定义了一个名为 sayHello 的最终方法:

javaCopy codepublic class MyClass {
    final void sayHello() {
        System.out.println("Hello!");
    }
}

3.final

和final方法一样

例如,下面的代码中定义了一个名为 MyClass 的最终类:

javaCopy codefinal public class MyClass {
    // ...
}

需要注意的是,使用 final 关键字可以提高程序的健壮性和安全性。常量一般用于定义不变的值,可以避免因变量被修改而引起的错误。最终方法和最终类可以避免子类重写或继承,可以保证框架的稳定性

继承语法

extends 和 implements 实现继承,且所有的类都是继承于 java.lang.Object,当一个类没有继承关键字,则默认继承 Object

extends

由于单继承,enxtends只能继承一个父类

public class Animal { 
    private String name;   
    private int id; 
    public Animal(String myName, int myid) { 
        //初始化属性值
    } 
    public void eat() {  //吃东西方法的具体实现  } 
    public void sleep() { //睡觉方法的具体实现  } 
} 
 
public class Penguin  extends  Animal{ 
}

implements

使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔

public interface A {
    public void eat();
    public void sleep();
}
 
public interface B {
    public void show();
}
 
public class C implements A,B {
}

Java重写与重载

重写Override

相当于覆盖+写(字面意思)

允许子类重新定义父类的方法,方法名、参数、都必须一致,返回类型可以一致或可以变小(数据类型自动转换)。重写方法时不能抛出新异常或比原来更广泛的异常

重写的一些注意事项:

1.访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected

2.父类的方法只能被它的子类重写

3.声明为 final 的方法不能被重写(后面补充关键字)

4.声明为 static 的方法不能被重写,但是能够被再次声明(后面补充关键字)

  • 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法
  • 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法

6.构造方法不能被重写(因为构造方法不能继承)

重载Overload

一个类中,方法名相同,而参数不同。返回类型可以相同也可以不同,称为重载

基础笔记中很多类的方法都是重载,这就不举例了

重载的一些注意事项:

1.被重载的方法必须改变参数

2.被重载的方法可以改变访问修饰符

3.被重载的方法可以声明更大的异常

重写与重载之间的区别

区别点重载方法重写方法
参数列表必须修改一定不能修改
返回类型可以修改一定不能修改
异常可以修改可以减少或删除,一定不能抛出新的或者更广的异常
访问可以修改一定不能做更严格的限制(可以降低限制)

Java抽象类

抽象类的意义

当一个父类具有不确定性时,就用抽象类来设计,或理解为一种规范。抽象类不能被实例化,由关键字abstract修饰

引例:

一个father父类:

public class father {
    public void speak(){
        System.out.println("父类不知道说什");
    }
}

和继承他的类

public class son extends father{
    @Override
    public void speak(){
        System.out.println("儿子说中文");
    }
}
public class daughter extends father {
    @Override
    public void speak(){
        System.out.println("女儿说英文");
    }
}

虽然子类继承并重写父类的方法,但父类的方法体一点用也没有,这就是父类方法不确定性

抽象方法

抽象方法在语法形式上,就是用abstract修饰且没有方法体的方法,使用了抽象方法的类一定是抽象类

父类的抽象方法,其意义在于对子类做了一个规范(必须拥有一个这样的方法),然后根据子类的不同,实现该方法也不同。之所以需要继承,是因为这个方法是这些子类的一个共同属性。或者说,父类要通过这些抽象的方法,提供某种特定的服务,但是实现方式在子类中有所不同,所以在父类中写了抽象方法强制子类实现,保证能够提供这种服务

抽象类特征归纳:

  1. 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象

  2. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类

  3. 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能

  4. 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法

  5. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类

Java接口

在 Java 中,有两种形式来达到抽象的目的,一种是抽象类,另外一种就是接口

接口是一系列方法的声明、是一些方法特征的集合(抽象),一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法

接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含需要类实现的方法

继承接口的类必须实现接口中所有方法(除非这个类是抽象类)

在jdk1.8以前是隐式抽象(方法默认会加上public abstract修饰),这样接口中只有抽象方法,jdk1.8以后多了默认方法和静态方法

接口语法(可略)

声明接口

public interface interface02 {
    void method();
}

public可见的,用其他也许(这就是idea中创建的格式)

public interface interface01 extends interface02{
    void speak();
}

可以使用interface关键字创建的同时还能extends继承一个接口,这样一来,实现该(interface01)接口的类不仅要实现这个接口本身,还要实现接口继承的接口

补充:接口可以多继承,使用一次extends后面多个接口用逗号隔开

接口实现

…implements 接口名称[, 其他接口名称, 其他接口名称…, …] …

public class daughter implements interface01{
    @Override
    public void speak() {

    }

    @Override
    public void method() {

    }
}

重写接口中声明的方法时,需要注意以下规则:

1.类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常

2.如果实现接口的类是抽象类,那么就没必要实现该接口的方法

3.在类实现接口的时候,也要注意一些规则:

一个类可以同时实现多个接口。

一个类只能继承一个类,但是能实现多个接口。

一个接口能继承另一个接口,这和类之间的继承比较相似

接口和类

接口和普通类的区别

  • 实现方式:类是具体的实现,它定义了对象的属性和方法,是一个具体的概念。而接口是一个抽象概念,只有定义没有实现
  • 访问控制:类可以有不同的访问修饰符,如public、protected、private等,而接口的所有方法和常量默认为public,并且不能使用其他修饰符
  • 继承关系:类是单继承(extends)而接口是多继承(implements )
  • 实例化:类可以实例化对象,而接口不能被实例化,只能被实现

接口和抽象类的区别

抽象类也是类,那么上文的区别依然存在,下面是一些补充:

  • 实现方式:抽象类是一个类,它可以包含成员变量和非抽象方法的实现,而接口只能包含抽象方法、常量和默认方法的签名,没有实际的实现
  • 抽象方法:抽象类中可以包含抽象方法,子类必须实现这些抽象方法才能被实例化。而接口中所有方法都是抽象的,实现接口的类必须实现所有方法
  • 构造函数:抽象类可以包含构造函数,接口不能包含构造函数
  • 使用场景:抽象类一般用于描述一些具体的事物,可以包含一些方法的实现,但是又需要子类来扩展和实现一些方法。而接口一般用于定义一些规范,使得不同的类可以实现同样的方法,以实现代码的复用

总之,抽象类和接口都是用于抽象化某些概念,但是它们在语法上有很大的不同,应根据实际需求选择使用抽象类或接口。通常情况下,如果需要提供一些默认的实现,或者想要描述一些具体的事物,就应该使用抽象类。而如果需要定义一些规范,或者要求不同的类实现相同的方法,就应该使用接口

Java枚举(enum)

是一种特殊的数据类型,它允许程序员定义自己的枚举类型。枚举类型是一组预定义常量的集合,这些常量在整个程序中是恒定的

语法

enum{ 
    RED, GREEN, BLUE; 
} 

例:

enum Color
{
    RED, GREEN, BLUE;
}
 
public class Test
{
    // 执行输出结果
    public static void main(String[] args)
    {
        Color c1 = Color.RED;
        System.out.println(c1);
    }
}

内部枚举

枚举也可以声明在类的内部

public class Test
{
    enum Color
    {
        RED, GREEN, BLUE;
    }
 
    // 执行输出结果
    public static void main(String[] args)
    {
        Color c1 = Color.RED;
        System.out.println(c1);
    }
}

迭代器枚举

enum Color
{
    RED, GREEN, BLUE;
}
public class MyClass {
  public static void main(String[] args) {
    for (Color myVar : Color.values()) {
      System.out.println(myVar);
    }
  }
}

Java封装

封装是将类的数据和方法封装在一起,并对外部程序隐藏内部细节,提供公共接口来访问类的功能。封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。封装可以有效保护数据的安全性,减少程序出错的风险

为什么要封装?封装的目的在于:

  1. 隐藏类的实现细节,只暴露必要的接口。通过封装,可以减少对类的了解程度,使类的使用更加简单,易于维护和升级
  2. 提高数据的安全性。封装可以限制对类的属性的访问,从而避免数据被非法修改或者误操作。这可以有效保护数据的完整性和安全性
  3. 减少程序出错的风险。封装可以将类的属性和方法组织成一个完整的整体,避免了代码重复和冗余,使代码更加简洁,易于维护

封装的实现

1.将属性私有

2.创建一对赋取值方法(get and set)并让方法公开以便外部访问

总结:set and get方法就是封装

Java多态

同一个方法在不同对象上可以有不同的实现方式。多态性有助于减少代码的冗余,增加代码的复用性和可维护性。在 Java 中,多态性主要通过继承和接口实现。具体地说,Java 多态性可以分为编译时多态性和运行时多态性两种形式

编译时多态性:在编译阶段确定方法的重载,即通过方法名和参数类型的不同来实现同一个方法的多种不同形式(所以方法重载和重写就是多态的表现)

运行时多态性:在运行阶段确定方法的实现,即通过继承和接口的方式实现方法的重写和多态性(使用父类引用指向子类对象,再调用某一父类中的方法时,不同的子类会表现出不同的结果)

例:

class Animal {
    public void makeSound() {
        System.out.println("动物发出声音");
    }
}

class Cat extends Animal {
    @Override
    //重写就是多态的表现(编译是多态性)
    public void makeSound() {
        System.out.println("猫发出喵喵的声音");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("狗发出汪汪的声音");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal a1 = new Animal();
        Animal a2 = new Cat();
        Animal a3 = new Dog();

        a1.makeSound();
        a2.makeSound();
        a3.makeSound();
        //↑运行时多态性
    }
}

多态存在的三个必要条件
1.继承
2.重写
3.父类引用指向子类对象:Parent p = new Child();

Java包

在Java中,包(Package)是一种用于组织和管理类和接口的机制。一个包包含了多个类和接口,这些类和接口可以被其他包或类使用。使用包可以避免类名的冲突,提高代码的复用性和可维护性

可以把包看做是一种文件夹的形式,用于存放相关的Java类文件。一个包中可以包含多个类文件,这些类文件可以被其他类或包引用。Java中的包是一个命名空间,用于解决类名的冲突问题,它可以使开发者更加方便地管理和组织类文件

Java中的包通常使用关键字package来定义。在一个Java类文件的开头,通常会定义该类所属的包名

包的作用

1、把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用

2、如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突

3、包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类

Java 使用包(package)这种机制是为了防止命名冲突,访问控制,提供搜索和定位类(class)、接口、枚举(enumerations)和注释(annotation)等

包的创建

创建包的时候,你需要为这个包取一个合适的名字。之后,如果其他的一个源文件包含了这个包提供的类、接口、枚举或者注释类型的时候,都必须将这个包的声明放在这个源文件的开头

包声明应该在源文件的第一行,每个源文件只能有一个包声明,这个文件中的每个类型都应用于它

如果一个源文件中没有使用包声明,那么其中的类,函数,枚举,注释等将被放在一个无名的包(unnamed package)中

总之,创建Java包的过程主要是在文件系统上创建一个与包名对应的文件夹,并在Java源代码文件的第一行使用package关键字声明该文件所属的包名

import关键字

import关键字是用来引入其他包中的类或接口的。使用import关键字可以使得我们在代码中可以直接使用其他包中的类或接口而不需要写出完整的包名。在Java中,所有的类都必须属于某一个包,如果没有显式地指定所属的包,那么它就属于默认包。默认包是不需要使用import关键字来引入的,因为它的作用域限定在同一个目录下。

需要注意的是,如果引入的类名与当前类中的成员变量或方法重名,那么需要使用完整的类名来区分它们。如果两个类中的类名相同,但是它们所在的包不同,那么也需要使用完整的类名来区分它们。

总之,import关键字是Java中用于引入其他包中的类或接口的关键字,它可以使代码更加简洁,避免重复书写完整的包名,方便管理和维护其他包中的类和接口

OOP总结

总结

Java是一种面向对象的编程语言,面向对象是Java的核心思想之一。面向对象是一种程序设计范型,它将程序的实现看作是一个对象的集合,这些对象相互作用以实现程序的功能。在面向对象编程中,程序被组织为类和对象,类是一种抽象的概念,它描述了一组具有相似属性和行为的对象。对象则是类的实例,它是具有一组特定属性和方法的实体。

在Java中,面向对象编程的基本概念包括:

  1. 类和对象:类是一种数据类型,它描述了对象的属性和方法。对象是类的一个实例,它具有类所定义的属性和方法。
  2. 封装:封装是指将数据和行为组合在一个单元中,从而避免对外暴露实现细节。在Java中,封装通过访问控制符来实现,可以将属性和方法设置为公有、私有或受保护的。
  3. 继承:继承是一种机制,它允许一个类继承另一个类的属性和方法。被继承的类称为父类或超类,继承的类称为子类或派生类。通过继承,子类可以重用父类的代码,并添加自己的属性和方法。
  4. 多态:多态是指同一方法可以在不同对象上有不同的实现方式。在Java中,多态可以通过继承、接口和泛型等方式来实现。

面向对象编程使代码更加灵活、可复用和可维护。Java的面向对象特性使得程序员可以通过抽象、封装、继承和多态等机制来设计和实现复杂的系统,这使得Java成为一种广泛应用于企业级应用、移动应用、游戏开发等领域的高级编程语言

面试题

Q:

面向过程和面向对象的区别

A:

面向过程的编程思想主要是从程序执行的角度出发,将程序看作一系列的函数调用和数据处理。是一种按照处理数据的过程来组织程序的编程方法,程序主要由一系列的函数组成。面向过程的编程方法更注重数据的处理,常常采用顺序执行的方式,比如刚开始学Java时写的计算器、输出内容,或者是Leetcode上写的算法题。Java仍然支持面向过程的编程方法。Java中可以使用静态方法(static method)和静态变量(static variable)来实现类级别的数据和函数处理,这种编程方法就具有一定的面向过程的特征。结论:因为类要实例化,性能需求大

而面向对象的编程思想主要是从问题本身的角度出发,将程序看作一系列的对象组成的系统,每个对象都有自己的状态和行为。程序的设计是基于对象的交互,将问题分解为多个对象,每个对象都有自己的属性和方法,然后编写类来定义对象的行为。结论:易维护、易复用、易拓展

总之,面向过程的编程适合于一些简单、线性、逐步执行的任务,而面向对象的编程适合于一些复杂、非线性、交互性强的系统。在实际开发中,Java作为一种面向对象的编程语言,具有封装、继承和多态等面向对象的特性,使得程序更加易于维护和扩展,能够更好地适应现代软件开发的需求

Q:

构造器(Constructor)可否重写(Override)

A:

Constructor(可以叫构造方法也可以叫构造器)是用来初始化对象的特殊方法,每个类都至少有一个构造方法,如果没有定义任何构造方法,编译器会自动生成一个默认的无参构造方法。在Java中,构造方法是不能被继承的,因此也就不能被重写。继承只是子类从父类继承了父类的属性和方法,并不包括构造方法

Q:

重载和重写的区别

A:

  1. 定义:重载是在同一个类中定义多个同名的方法,但它们的参数类型或参数个数不同;而重写是在子类中重写父类的方法,方法名、参数列表和返回类型都相同
  2. 发生时间:重载是在编译时发生的,编译器根据方法的参数列表来区分同名的方法;而重写是在运行时发生的,根据实际对象类型来确定调用哪个方法
  3. 目的:重载的目的是提供更多的方法来处理不同类型的参数,可以提高程序的灵活性和复用性;而重写的目的是扩展或修改从父类继承的方法的行为,实现多态性
  4. 返回类型:重载方法的返回类型可以相同,也可以不同,只要方法签名不同;而重写方法的返回类型必须相同
  5. 访问修饰符:重载方法的访问修饰符可以不同,但不能比父类的访问修饰符更严格;而重写方法的访问修饰符不能比父类更严格,只能相同或更宽松

Q:

什么是自动拆箱与装箱

A:

自动装箱是指将基本数据类型转换成对应的包装类对象,例:

int a = 10;
Integer b = a; // 自动装箱,将int类型的变量a转换为Integer类型的对象

自动拆箱是指将包装类对象转换成对应的基本数据类型,例:

Integer a = 10;
int b = a; // 自动拆箱,将Integer类型的对象a转换为int类型的基本数据类型

自动拆箱和装箱让程序员在使用基本数据类型和其对应的包装类时更加方便,能够提高程序的可读性和可维护性。但需要注意的是,在自动拆箱时,如果包装类对象为null,则会抛出NullPointerException异常

Q:

怎么理解面向对象编程的三大特性

A:

  1. 封装(Encapsulation):封装是一种将数据和方法组合在一个单元内的机制,以保护数据不受外部直接访问和修改。在面向对象编程中,类就是一个封装了数据和方法的单元,只有类内部的方法才能访问和修改类的数据,从而保证了数据的安全性和完整性。封装的好处在于,它能够降低类与类之间的耦合性,增加代码的可维护性和可重用性
  2. 继承(Inheritance):继承是一种从已有类派生出新类的机制。通过继承,子类可以从父类中继承属性和方法,并且可以根据需要添加新的属性和方法。继承的好处在于,它能够提高代码的重用性和可扩展性,避免代码的重复编写,同时也能够提高程序的可读性和可维护性
  3. 多态(Polymorphism):多态是一种让不同的对象对同一消息作出不同响应的机制。在面向对象编程中,多态性使得可以通过一个通用的接口来操作不同的对象,从而提高程序的灵活性和可扩展性。多态的实现方式包括方法重载和方法重写

Q:

怎么理解要创建一个无参构造器

A:

在Java中,每个类都必须有一个构造方法,如果程序员没有显式地定义构造方法,则编译器会自动(默认)生成一个无参构造方法,所以父类必须有一个无参构造器(所以在写业务的时候同时需要 有参和无参,有参用于赋值),父类没有无参,编译则不通过

补充:什么是:没有显式地定义构造方法,例

public class child extends father {
    private String age;
    public child(String age){
        this.age = age;
        //上述代码隐藏了super();这就是没有显式地定义构造方法
    }
}

Q:

成员变量与局部变量的区别

A:

成员变量是定义在类中的变量,也称为实例变量或字段。与局部变量不同,成员变量的作用域不仅限于类的某个方法或代码块中,而是在整个类中都可以访问。每个类的实例都有自己的一组成员变量副本,因此成员变量的值在不同的对象实例中可以不同

在 Java 中,成员变量可以分为以下两种类型:

实例变量

实例变量是定义在类中,但在方法之外的变量。每个实例都有自己的实例变量副本,它们在对象创建时被初始化,并且可以通过对象来访问。

例如,下面的代码中定义了一个名为 Person 的类,其中定义了两个实例变量 nameage

javaCopy codepublic class Person {
    String name;
    int age;
}

静态变量(前文static关键字已经介绍了,这里内容重复)

静态变量是定义在类中,但使用 static 关键字修饰的变量。它们在类加载时被初始化,并且只有一份副本,被类的所有实例共享。可以通过类名来访问静态变量。

例如,下面的代码中定义了一个名为 Counter 的类,其中定义了一个静态变量 count

javaCopy codepublic class Counter {
    static int count = 0;
}

需要注意的是,成员变量的访问权限可以使用访问修饰符 publicprivateprotected 或者默认访问修饰符控制,访问权限控制了成员变量的可见性和可访问性。成员变量可以存储对象的状态和属性,它们是面向对象编程中重要的组成部分

局部变量是在方法、代码块或者构造方法中定义的变量,只能在它们所在的作用域内访问。作用域是指变量被定义后能够被访问的区域

局部变量只在它所在的代码块中有效,出了这个代码块就失效了。局部变量必须要初始化后才能使用,否则编译器会报错。在方法调用结束时,局部变量的值会被销毁,这样可以节省内存空间

例如,下面的代码中定义了一个名为 add 的方法,其中定义了两个局部变量 ab

javaCopy codepublic int add(int a, int b) {
    int sum = a + b; // 定义局部变量 sum
    return sum;
}

在这个例子中,ab 是方法的参数,也是局部变量,它们的作用域仅限于方法中。sum 是在方法中定义的局部变量,作用域仅限于方法中的代码块,方法调用结束后,它的值会被销毁

Q:

==和equals

Object类的equals方法默认实现是比较两个对象的引用是否相等,这就导致了obejct类的equals方法和==是一致的。但是java自带的类:string类或者其他基本类型,就重写了equals方法,是比较它们的值是否相等。所以在自己类要是有equals方法时就要重写,以下列举了大多数要重写equals的情况:

1.创建String字符串

在Java中,String对象是不可变的,也就是说,当我们创建一个新的字符串时,实际上是创建了一个新的对象。因此,如果我们使用默认的equals方法来比较两个字符串对象,那么只有当它们的引用相同时,才会返回true。这显然是不合适的,因为我们通常需要根据字符串的内容来判断它们是否相等

2.自定义对象比较

假设我们定义了一个Person类来表示人员信息,它有name和age两个属性。如果我们使用默认的equals方法来比较两个Person对象,那么只有当它们的引用相同时,才会返回true。但实际上我们需要的是比较这两个属性是否相等。(就算两个属性相对,引用不同也不会返回true)

所以需要重写equals方法

而==是操作符,用于比较两个对象的引用是否相等。如果两个对象引用同一个对象,则返回true,否则返回false。在equals不重写的前提下,二者mei’you

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值