02.Java面向对象问题

2,130 阅读29分钟

目录介绍

  • 2.0.0.1 面向对象编程的四大特性及其含义?封装、继承、多态分别是什么?为何要封装?为什么是单继承而不能多继承呢?
  • 2.0.0.2 重载和重写的区别?重载和重写绑定机制有何区别?父类的静态方法能否被子类重写?重写是动态绑定,如何理解机制?
  • 2.0.0.3 什么是绑定?静态和动态绑定如何区别?动态绑定编译原理是什么?动态绑定运行原理是什么?
  • 2.0.0.4 接口和抽象类的区别是什么?接口的意义是什么?抽象类的意义是什么?如何选择抽象类和接口?
  • 2.0.0.5 为什么内部类调用的外部变量必须是final修饰的?局部变量对垃圾回收机制有什么样的影响?
  • 2.0.0.7 什么是多态?多态的实现方式有哪些?多态有哪些弊端?Java实现多态有哪些必要条件?多态的实现原理?
  • 2.0.0.9 静态变量和成员变量的区别?代码块有哪些?构造代码块和构造方法哪一个先执行?
  • 2.0.0.8 将不同对象分类的服务方法进行抽象,把业务逻辑的紧耦合关系拆开,实现代码的隔离保证了方便的扩展?
  • 2.0.1.0 抽象类具有什么特点?抽象类和普通类有何区别?抽象类可以new吗?会出现什么问题?
  • 2.0.1.1 什么是内部类,有哪些?静态内部类和非静态内部类的区别?内部类作用的作用主要是什么?内部类和外部类联系?

好消息

  • 博客笔记大汇总【15年10月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计500篇[近100万字],将会陆续发表到网上,转载请注明出处,谢谢!
  • 链接地址:github.com/yangchong21…
  • 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!所有博客将陆续开源到GitHub!

2.0.0.1 面向对象编程的四大特性及其含义?封装、继承、多态分别是什么?为何要封装?为什么是单继承而不能多继承呢?

  • 封装
    • 将某事物的属性和行为包装到对象中,构成一个不可分割的独立实体。
    • 封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。
  • 继承
    • 继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。
    • 注意技术博客大总结
      • 子类拥有父类非 private 的属性和方法。
      • 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
      • 子类可以用自己的方式实现父类的方法。
  • 多态
    • 所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
    • 在Java中有两种形式可以实现多态:继承(多个子类对同一方法的重写)和接口(实现接口并覆盖接口中同一方法)。
  • 抽象
    • 对现实世界的事物进行概括,抽象为在计算机虚拟世界中有意义的实体
  • 为什么是单继承而不能多继承呢?
    • 若为多继承,那么当多个父类中有重复的属性或者方法时,子类的调用结果会含糊不清,因此用了单继承。
    • 多继承虽然能使子类同时拥有多个父类的特征,但是其缺点也是很显著的,主要有两方面:
      • 如果在一个子类继承的多个父类中拥有相同名字的实例变量,子类在引用该变量时将产生歧义,无法判断应该使用哪个父类的变量。
      • 如果在一个子类继承的多个父类中拥有相同方法,子类中有没有覆盖该方法,那么调用该方法时将产生歧义,无法判断应该调用哪个父类的方法。
    • Java是从C++语言上优化而来,而C++也是面向对象的,为什么它却可以多继承的呢?首先,C++语言是1983年在C语言的基础上推出的,Java语言是1995年推出的。其次,在C++被设计出来后,就会经常掉入多继承这个陷阱,虽然它也提出了相应的解决办法,但Java语言本着简单的原则舍弃了C++中的多继承,这样也会使程序更具安全性。
  • 为什么是多实现呢?技术博客大总结
    • 通过实现接口拓展了类的功能,若实现的多个接口中有重复的方法也没关系,因为实现类中必须重写接口中的方法,所以调用时还是调用的实现类中重写的方法。

2.0.0.2 重载和重写的区别?重载和重写绑定机制有何区别?父类的静态方法能否被子类重写?重写是动态绑定,如何理解机制?

  • 重载
    • 发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
  • 重写
    • 重写表示子类重写父类的方法
    • 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法。
  • 重载和重写绑定机制有何区别?技术博客大总结
    • 重载:类内多态,静态绑定机制(编译时已经知道具体执行哪个方法),方法同名,参数不同
    • 重写:类间多态,动态绑定机制(运行时确定),实例方法,两小两同一大(方法签名相同,子类的方法所抛出的异常、返回值的范围不大于父类的对应方法,子类的方法可见性不小于父类的对应方法)方法签名相同,子类的方法所抛出的异常、返回值的范围不大于父类的对应方法,子类的方法可见性不小于父类的对应方法
  • 父类的静态方法能否被子类重写?
    • 父类的静态方法是不能被子类重写的,其实重写只能适用于实例方法,不能用于静态方法,对于上面这种静态方法而言,我们应该称之为隐藏。
    • 技术博客大总结
    • Java静态方法形式上可以重写,但从本质上来说不是Java的重写。因为静态方法只与类相关,不与具体实现相关。声明的是什么类,则引用相应类的静态方法(本来静态无需声明,可以直接引用)。并且static方法不是后期绑定的,它在编译期就绑定了。换句话说,这个方法不会进行多态的判断,只与声明的类有关。
  • 重写是动态绑定,如何理解机制?
    • 在程序运行过程中,根据具体的实例对象才能具体确定是哪个方法。
    • 动态绑定是多态性得以实现的重要因素,它通过方法表来实现:每个类被加载到虚拟机时,在方法区保存元数据,其中,包括一个叫做 方法表(method table)的东西,表中记录了这个类定义的方法的指针,每个表项指向一个具体的方法代码。如果这个类重写了父类中的某个方法,则对应表项指向新的代码实现处。从父类继承来的方法位于子类定义的方法的前面。

2.0.0.3 什么是绑定?静态和动态绑定如何区别?动态绑定编译原理是什么?动态绑定运行原理是什么?

  • 什么是绑定?
    • 把一个方法与其所在的类/对象 关联起来叫做方法的绑定。绑定分为静态绑定(前期绑定)和动态绑定(后期绑定)。
  • 静态和动态绑定如何区别?
    • 静态绑定(前期绑定)是指:
      • 在程序运行前就已经知道方法是属于那个类的,在编译的时候就可以连接到类的中,定位到这个方法。
      • 在Java中,final、private、static修饰的方法以及构造函数都是静态绑定的,不需程序运行,不需具体的实例对象就可以知道这个方法的具体内容。
    • 动态绑定(后期绑定)是指:
      • 在程序运行过程中,根据具体的实例对象才能具体确定是哪个方法。
      • 动态绑定是多态性得以实现的重要因素,它通过方法表来实现:每个类被加载到虚拟机时,在方法区保存元数据,其中,包括一个叫做 方法表(method table)的东西,表中记录了这个类定义的方法的指针,每个表项指向一个具体的方法代码。如果这个类重写了父类中的某个方法,则对应表项指向新的代码实现处。从父类继承来的方法位于子类定义的方法的前面。
  • 动态绑定编译原理技术博客大总结
    • 我们假设 Father ft=new Son(); ft.say(); Son继承自Father,重写了say()。
    • 编译:我们知道,向上转型时,用父类引用执行子类对象,并可以用父类引用调用子类中重写了的同名方法。但是不能调用子类中新增的方法,为什么呢?
    • 因为在代码的编译阶段,编译器通过 声明对象的类型(即引用本身的类型) 在方法区中该类型的方法表中查找匹配的方法(最佳匹配法:参数类型最接近的被调用),如果有则编译通过。(这里是根据声明的对象类型来查找的,所以此处是查找 Father类的方法表,而Father类方法表中是没有子类新增的方法的,所以不能调用。)
    • 编译阶段是确保方法的存在性,保证程序能顺利、安全运行。
  • 动态绑定运行原理是什么?
    • 运行:我们又知道,ft.say()调用的是Son中的say(),这不就与上面说的,查找Father类的方法表的匹配方法矛盾了吗?不,这里就是动态绑定机制的真正体现。
    • 上面编译阶段在 声明对象类型 的方法表中查找方法,只是为了安全地通过编译(也为了检验方法是否是存在的)。而在实际运行这条语句时,在执行 Father ft=new Son(); 这一句时创建了一个Son实例对象,然后在 ft.say() 调用方法时,JVM会把刚才的son对象压入操作数栈,用它来进行调用。而用实例对象进行方法调用的过程就是动态绑定:根据实例对象所属的类型去查找它的方法表,找到匹配的方法进行调用。我们知道,子类中如果重写了父类的方法,则方法表中同名表项会指向子类的方法代码;若无重写,则按照父类中的方法表顺序保存在子类方法表中。故此:动态绑定根据对象的类型的方法表查找方法是一定会匹配(因为编译时在父类方法表中以及查找并匹配成功了,说明方法是存在的。这也解释了为何向上转型时父类引用不能调用子类新增的方法:在父类方法表中必须先对这个方法的存在性进行检验,如果在运行时才检验就容易出危险——可能子类中也没有这个方法)。技术博客大总结
  • 两者之间区分
    • 程序在JVM运行过程中,会把类的类型信息、static属性和方法、final常量等元数据加载到方法区,这些在类被加载时就已经知道,不需对象的创建就能访问的,就是静态绑定的内容;需要等对象创建出来,使用时根据堆中的实例对象的类型才进行取用的就是动态绑定的内容。

2.0.0.3 接口和抽象类的区别是什么?接口的意义是什么?抽象类的意义是什么?如何选择抽象类和接口?

  • 接口和抽象类的区别是什么
    • 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),抽象类可以有非抽象的方法
    • 接口中的实例变量默认是 final 类型的,而抽象类中则不一定
    • 一个类可以实现多个接口,但最多只能实现一个抽象类
    • 一个类实现接口的话要实现接口的所有方法,而抽象类不一定
    • 接口不能用 new 实例化,但可以声明,但是必须引用一个实现该接口的对象 从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。
  • 接口的作用是什么
    • 技术博客大总结
    • 1、重要性:在Java语言中, abstract class 和interface 是支持抽象类定义的两种机制。正是由于这两种机制的存在,才赋予了Java强大的 面向对象能力。
    • 2、简单、规范性:如果一个项目比较庞大,那么就需要一个能理清所有业务的架构师来定义一些主要的接口,这些接口不仅告诉开发人员你需要实现那些业务,而且也将命名规范限制住了(防止一些开发人员随便命名导致别的程序员无法看明白)。
    • 3、维护、拓展性:比如你要做一个画板程序,其中里面有一个面板类,主要负责绘画功能,然后你就这样定义了这个类。可是在不久将来,你突然发现这个类满足不了你了,然后你又要重新设计这个类,更糟糕是你可能要放弃这个类,那么其他地方可能有引用他,这样修改起来很麻烦。
    • 如果你一开始定义一个接口,把绘制功能放在接口里,然后定义类时实现这个接口,然后你只要用这个接口去引用实现它的类就行了,以后要换的话只不过是引用另一个类而已,这样就达到维护、拓展的方便性。
    • 4、安全、严密性:接口是实现软件松耦合的重要手段,它描叙了系统对外的所有服务,而不涉及任何具体的实现细节。这样就比较安全、严密一些(一般软件服务商考虑的比较多)。
  • 抽象类的意义是什么
    • 1.因为抽象类不能实例化对象,所以必须要有子类来实现它之后才能使用。这样就可以把一些具有相同属性和方法的组件进行抽象,这样更有利于代码和程序的维护。
    • 2.当又有一个具有相似的组件产生时,只需要实现该抽象类就可以获得该抽象类的那些属性和方法。
  • 如何选择抽象类和接口?
    • 使用接口:技术博客大总结
      • 需要让不相关的类都实现一个方法,例如不相关的类都可以实现 Compareable 接口中的 compareTo() 方法;
      • 需要使用多重继承。
    • 使用抽象类:
      • 需要在几个相关的类中共享代码。
      • 需要能控制继承来的成员的访问权限,而不是都为 public。
      • 需要继承非静态和非常量字段。

2.0.0.4 什么是内部类,有哪些?静态内部类和非静态内部类的区别?内部类作用的作用主要是什么?内部类和外部类联系?

  • 什么是内部类
    • 内部类就是定义在另外一个类里面的类。它隐藏在外部类中,封装性更强,不允许除外部类外的其他类访问它;但它可直接访问外部类的成员。
  • 内部类有哪些
    • 成员内部类:成员内部类是外围类的一个成员,是依附于外围类的,所以,只有先创建了外围类对象才能够创建内部类对象。也正是由于这个原因,成员内部类也不能含有 static 的变量和方法;
    • 静态内部类:静态内部类,就是修饰为static的内部类,该内部类对象不依赖于外部类对象,就是说我们可以直接创建内部类对象,但其只可以直接访问外部类的所有静态成员和静态方法;
    • 局部内部类:局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效;技术博客大总结
    • 匿名内部类:定义匿名内部类的前提是,内部类必须要继承一个类或者实现接口,格式为 new 父类或者接口(){定义子类的内容(如函数等)}。也就是说,匿名内部类最终提供给我们的是一个 匿名子类的对象。
  • 静态内部类和非静态内部类的区别有:
    • 静态内部类是指被声明为static的内部类,可不依赖外部类实例化;而非静态内部类需要通过生成外部类来间接生成。
    • 静态内部类只能访问外部类的静态成员变量和静态方法,而非静态内部类由于持有对外部类的引用,可以访问外部类的所用成员
  • 内部类作用的作用主要是什么?
    • 内部类作用主要实现功能的隐藏、减少内存开销,提高程序的运行速度
  • 内部类和外部类联系:
    • 内部类可以访问外部类所有的方法和属性,如果内部类和外部类有相同的成员方法和成员属性,内部类的成员方法调用要优先于外部类即内部类的优先级比较高(只限于类内部,在主方法内,内部类对象不能访问外部类的成员方法和成员属性),外部类只能访问内部类的静态常量或者通过创建内部类来访问内部类的成员属性和方法

2.0.0.5 为什么内部类调用的局部变量必须是final修饰的?局部变量对垃圾回收机制有什么样的影响?

  • 为什么内部类调用的外部变量必须是final修饰的?
    • 简单解答:一方面,由于方法中的局部变量的生命周期很短,一旦方法结束变量就要被销毁,为了保证在内部类中能找到外部局部变量,通过final关键字可得到一个外部变量的引用;另一方面,通过final关键字也不会在内部类去做修改该变量的值,保护了数据的一致性。
    • 详细一点可以这样说:因为生命周期的原因。方法中的局部变量,方法结束后这个变量就要释放掉,final保证这个变量始终指向一个对象。首先,内部类和外部类其实是处于同一个级别,内部类不会因为定义在方法中就会随着方法的执行完毕而跟随者被销毁。问题就来了,如果外部类的方法中的变量不定义final,那么当外部类方法执行完毕的时候,这个局部变量肯定也就被GC了,然而内部类的某个方法还没有执行完,这个时候他所引用的外部变量已经找不到了。如果定义为final,java会将这个变量复制一份作为成员变量内置于内部类中,这样的话,由于final所修饰的值始终无法改变,所以这个变量所指向的内存区域就不会变。
    • 为了解决:局部变量的生命周期与局部内部类的对象的生命周期的不一致性问题
    • 注意:在java 1.8中,可以不用final修饰,但是千万不要被误导,因为你不用final修饰,在匿名内部类中修改它的值还是会导致编译报错。因为java 1.8其实会自动给它加上final
  • 局部变量对垃圾回收机制有什么样的影响?技术博客大总结
    • 先说出结论:局部变量表中的变量是很重要的垃圾回收根节点,被局部变量表中变量直接或者间接引用的对象都不会被回收。

2.0.0.7 什么是多态?多态的实现方式有哪些?多态有哪些弊端?Java实现多态有哪些必要条件?多态的实现原理?

  • 什么是多态?
    • 多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
  • 多态实现条件?技术博客大总结
    • Java实现多态有三个必要条件:继承、重写、向上转型。
    • 继承:在多态中必须存在有继承关系的子类和父类。
    • 重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
    • 向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。
  • 多态的实现方式有哪些?
    • 多态作用:多态性就是相同的消息使得不同的类做出不同的响应。
    • 第一种实现方式:基于继承实现的多态
      • 基于继承的实现机制主要表现在父类和继承该父类的一个或多个子类对某些方法的重写,多个子类对同一方法的重写可以表现出不同的行为。多态的表现就是不同的对象可以执行相同的行为,但是他们都需要通过自己的实现方式来执行,这就要得益于向上转型了。
      public class MainJava {
          public static void main(String[] args) {
              //定义父类数组
              Wine[] wines = new Wine[2];
              //定义两个子类
              Test1 test1 = new Test1();
              Test2 test2 = new Test2();
              Wine win e = new Wine();
              //父类引用子类对象
              wines[0] = test1;
              wines[1] = test2;
              for(int i = 0 ; i < 2 ; i++){
                  System.out.println(wines[i].toString() + "--" + wines[i].drink());
              }
              System.out.println("-------------------------------");
              System.out.println(test1.toString() + "--" + test1.drink());
              System.out.println(test2.toString() + "--" + test2.drink());
          }
          public static class Wine {
              private String name;
              public String getName() {
                  return name;
              }
              public void setName(String name) {
                  this.name = name;
              }
              public String drink(){
                  return "喝的是 " + getName();
              }
              public String toString(){
                  return null;
              }
          }
      
          public static class Test1 extends Wine{
              public Test1(){
                  setName("Test1");
              }
              public String drink(){
                  return "喝的是 " + getName();
              }
              public String toString(){
                  return "Wine : " + getName();
              }
          }
      
          public static class Test2 extends Wine{
              public Test2(){
                  setName("Test2");
              }
              public String drink(){
                  return "喝的是 " + getName();
              }
              public String toString(){
                  return "Wine : " + getName();
              }
          }
      }
      
    • 第二种实现多态的方式:基于接口实现的多态
      • 继承是通过重写父类的同一方法的几个不同子类来体现的,那么就可就是通过实现接口并覆盖接口中同一方法的几不同的类体现的。
      • 在接口的多态中,指向接口的引用必须是指定这实现了该接口的一个类的实例程序,在运行时,根据对象引用的实际类型来执行对应的方法。
      • 继承都是单继承,只能为一组相关的类提供一致的服务接口。但是接口可以是多继承多实现,它能够利用一组相关或者不相关的接口进行组合与扩充,能够对外提供一致的服务接口。所以它相对于继承来说有更好的灵活性。
  • 多态有哪些弊端?技术博客大总结
    • 不转型的话,不能使用子类特有的属性和行为
    class Demo_SuperMan {
        public static void main(String[]args){
            Person p=new SuperMan();//父类引用指向子类对象。超人提升为了人
                                    //父类引用指向子类对象,就是向上转型
            System.out.println(p.name);
            p.Tsy();
            //p.Fly();//找不到该方法
            SuperMan sm=(SuperMan)p;//向下转型,看到整个对象的内容
            sm.Fly();
        }
    }
     
    class Person{
        String name="John";
        public void Tsy(){
            System.out.println("Tsy");
        }
    }
     
    class SuperMan extends Person{
        String name="SuperName";
        @Override
        public void Tsy(){
            System.out.println("子类Tsy");
        }
     
        public void Fly(){
            System.out.println("飞出去救人");
        }
    }
    

2.0.0.8 将不同对象分类的服务方法进行抽象,把业务逻辑的紧耦合关系拆开,实现代码的隔离保证了方便的扩展?

  • 看看下面这段代码,改编某伟大公司产品代码,你觉得可以利用面向对象设计原则如何改进?
    public class VIPCenter {
        void serviceVIP(T extend User user>) {
         if (user instanceof SlumDogVIP) {
            // 穷 X VIP,活动抢的那种
            // do somthing
          } else if(user instanceof RealVIP) {
            // do somthing
          }
          // ...
    }
    
    • 这段代码的一个问题是,业务逻辑集中在一起,当出现新的用户类型时,比如,大数据发现了我们是肥羊,需要去收获一下, 这就需要直接去修改服务方法代码实现,这可能会意外影响不相关的某个用户类型逻辑。
    • 利用开关原则,可以尝试改造为下面的代码。将不同对象分类的服务方法进行抽象,把业务逻辑的紧耦合关系拆开,实现代码的隔离保证了方便的扩展。技术博客大总结
    public class VIPCenter {
        private Map<User.TYPE, ServiceProvider> providers;
        void serviceVIP(T extend User user) {
            providers.get(user.getType()).service(user);
        }
    }
    
    interface ServiceProvider{
        void service(T extend User user) ;
    }
    
    class SlumDogVIPServiceProvider implements ServiceProvider{
        void service(T extend User user){
            // do somthing
        }
    }
    
    class RealVIPServiceProvider implements ServiceProvider{
        void service(T extend User user) {
            // do something
        }
    }
    

2.0.0.9 静态变量和成员变量的区别?代码块有哪些?构造代码块和构造方法哪一个先执行?

  • 静态变量和成员变量的区别
    • A:所属不同
      • 静态变量属于类,所以也称为类变量
      • 成员变量属于对象,所以也称为实例变量(对象变量)
    • B:内存中位置不同
      • 静态变量存储于方法区的静态区
      • 成员变量存储于堆内存
    • C:内存出现时间不同技术博客大总结
      • 静态变量随着类的加载而加载,随着类的消失而消失
      • 成员变量随着对象的创建而存在,随着对象的消失而消失
    • D:调用不同
      • 静态变量可以通过类名调用,也可以通过对象调用
      • 成员变量只能通过对象名调用
  • 代码块有哪些
    • A:代码块概述
      • 在Java中,使用{}括起来的代码被称为代码块。
    • B:代码块分类
      • 根据其位置和声明的不同,可以分为局部代码块,构造代码块,静态代码块,同步代码块。
    • C:常见代码块的应用
      • a:局部代码块
        • 在方法中出现;限定变量生命周期,及早释放,提高内存利用率
      • b:构造代码块
        • 在类中方法外出现;多个构造方法方法中相同的代码存放到一起,每次调用构造都执行,并且在构造方法前执行
      • c:静态代码块
        • 在类中方法外出现,加了static修饰
        • 在类中方法外出现,并加上static修饰;用于给类进行初始化,在加载的时候就执行,并且只执行一次。
  • 构造代码块和构造方法哪一个先执行?

2.0.1.0 抽象类具有什么特点?抽象类和普通类有何区别?抽象类可以new吗?会出现什么问题?

  • 抽象类具有什么特点技术博客大总结
    • 抽象类和抽象方法都使用 abstract 关键字进行声明。抽象类一般会包含抽象方法,抽象方法一定位于抽象类中。
  • 抽象类和普通类有何区别
    • 抽象类和普通类最大的区别是,抽象类不能被实例化,需要继承抽象类才能实例化其子类。
    public abstract class AbstractClassExample {
        protected int x;
        private int y;
        public abstract void func1();
    
        public void func2() {
            System.out.println("func2");
        }
    }
    
    public class AbstractExtendClassExample extends AbstractClassExample {
        @Override
        public void func1() {
            System.out.println("func1");
        }
    }
    
  • 抽象类可以new吗?会出现什么问题?
    • 注意抽象类是不能被实例化的,也就是不能new出来的!
    • 如果执意需要new,则会提示
    • image

2.0.1.1 Java数据类型有哪些?什么是值传递?什么是引用传递?如何理解值传递和引用传递,以及它们有何区别?

  • Java数据类型有哪些?技术博客大总结
    • Java中的数据类型分为两种为基本类型和引用类型。
      • 1、基本类型的变量保存原始值,所以变量就是数据本身。
        • 常见的基本类型:byte,short,int,long,char,float,double,Boolean,returnAddress。
      • 2、引用类型的变量保存引用值,所谓的引用值就是对象所在内存空间的“首地址值”,通过对这个引用值来操作对象。
        • 常见的引用类型:类类型,接口类型和数组。
    • 基本类型(primitive types)
      • primitive types 包括boolean类型以及数值类型(numeric types)。numeric types又分为整型(integer types)和浮点型(floating-point type)。整型有5种:byte short int long char(char本质上是一种特殊的int)。浮点类型有float和double。
    • 引用类型(reference types)
      • ①接口 ②类 ③数组
  • 什么是值传递?
    • 在方法的调用过程中,实参把它的实际值传递给形参,此传递过程就是将实参的值复制一份传递到函数中,这样如果在函数中对该值(形参的值)进行了操作将不会影响实参的值。因为是直接复制,所以这种方式在传递大量数据时,运行效率会特别低下。
    • 比如String类,设计成不可变的,所以每次赋值都是重新创建一个新的对象,因此是值传递!
  • 什么是引用传递?技术博客大总结
    • 引用传递弥补了值传递的不足,如果传递的数据量很大,直接复过去的话,会占用大量的内存空间。
    • 引用传递就是将对象的地址值传递过去,函数接收的是原始值的首地址值。
    • 在方法的执行过程中,形参和实参的内容相同,指向同一块内存地址,也就是说操作的其实都是源数据,所以方法的执行将会影响到实际对象。
  • 如何理解值传递和引用传递,以及它们有何区别?
    • 看下面代码案例
      private void test1(){
          Demo demo = new Demo();
          demo.change(demo.str, demo.ch);
          Log.d("yc---",demo.str);
          Log.d("yc---", Arrays.toString(demo.ch));
          //打印值
          //yc---: hello
          //yc---: [c, b]
      }
      
      public class Demo {
          String str = new String("hello");
          char[] ch = {'a', 'b'};
          public void change(String str, char[] ch) {
              str = "ok";
              ch[0] = 'c';
          }
      }
      
    • 案例过程分析
      • 为对象分配空间
        • image
      • 执行change()方法
        • 执行前实参(黑色)和形参(红色)的指向如下:
        • image
      • 最后打印
        • 因为String是不可变类且为值传递,而ch[]是引用传递,所以方法中的str = "ok",相当于重新创建一个对象并没有改变实参str的值,数组是引用传递,直接改变,所以执行完方法后,指向关系如下:
        • image
  • 通过上面的分析我们可以得出以下结论:技术博客大总结
    • 基本数据类型传值,对形参的修改不会影响实参;
    • 引用类型传引用,形参和实参指向同一个内存地址(同一个对象),所以对参数的修改会影响到实际的对象。
    • String, Integer, Double等immutable的类型特殊处理,可以理解为传值,最后的操作不会修改实参对象。
  • 如何理解引用类型的按值传递?
    • 引用类型的按值传递,传递的是对象的地址。只是得到元素的地址值,并没有复制元素。比如数组,就是引用传递,假如说是值传递,那么在方法调用赋值中,将实参的值复制一份传递到函数中将会非常影响效率。

其他介绍

01.关于博客汇总链接

02.关于我的博客