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

对比面向过程,首先是两种不同处理问题的角度
面向过程更注重事情的每一个步骤以及顺序,而面向对象更注重事情的参与者(对象)以及各自需要做些什么
比如:洗衣机洗衣服
面向过程会将洗衣服拆分成为一系列的步骤(函数)打开洗衣机 —> 放衣服 —> 放洗衣液 —> 清洗 —> 晒干
面向对象会拆分为人和洗衣机两个对象:
人需要做:打卡洗衣机——> 放洗衣液
洗衣机需要做:清洗——> 晒干
从上述对比中可以发现,面向过程比较直观高效,而面向对象易于复用、扩展和维护

面向对象的三大特性

  • 封装:封装的意义在于明确标识出允许外部使用的所有成员函数和数据项,内部细节对外部透明,而外部调用无需修改或关心内部实现
  • 继承:继承基类的方法,并做出自己的改变或者扩展,子类共性的方法或者属性直接使用父类的,而不需要自己再定义,只需要扩展自己个性化的
  • 多态:基于对象所属类的不同,外部对同一个方法的调用,实际执行的逻辑不同,而多态的弊端是无法调用子类特有的功能。

    JDK、JRE、JVM三者的区别和联系

    JDK:JavaDevelopment Kit Java开发工具
    JRE:Java Runtime Environment Java运行时环境
    JVM:Virtual Machine Java虚拟机

安装Java后在安装目录中的bin目录和lib目录其实就是JRE
image.png
image.png
解释:一次开发到处运行(Java跨平台原理),由我们创建开发的Java类由Java工具中的Javac编译后形成.class文件,放到不同的操作系统上时会调用lib类库解释class后映射到系统调用

  1. java 源程序(.java)要先编译成为与平台无关的字节码文件(.class),
  2. 然后字节码文件再解释成为机器码运行,解释是通过Java虚拟机来执行的,
  3. 字节码文件不面向任何具体平台,只面向虚拟机。Java虚拟机是可运行
  4. Java字节码文件的虚拟计算机,不同平台的虚拟机是不同的,但它们都提供了
  5. 相同的接口。

image.png

== 和 equals

== :
对于基本数据类型和引用数据类型 ==的作用效果是不同的
基本类型:比较的是值是否相等
引用类型:比较的是引用是否相等
equals:
equals 本质上就是 == ,只不过String 和 Integer 等重写了equals方法,把它变成了值比较

简述final 作用

final:最终的

  • 修饰类:表示类不可以被继承
  • 修饰方法:表示方法不能被重写、但可以被重载
  • 修饰变量:表示变量一旦被赋值就不可以更改其值

(1)修饰成员变量

  • 如果final修饰的是类变量,只能在静态初始化块中指定初始值或者声明该类变量时所指定的初始值
  • 如果final修饰的是成员变量,可以在非静态初始化块、声明该变量或者构造器中的执行初始化值

(2)修饰局部变量
系统不会为局部变量进行初始化,局部变量必须由程序员显示初始值,因此使用final修饰局部变量时,即可以在定义时指定默认值(后面的代码不能对变量再赋值),也可以不指定默认值,而在后面的代码中对final变量赋初始值(仅一次)
(3)修饰基本类型数据和引用类型数据

  • 如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改
  • 如果是引用数据类型,则在对其初始化之后便不能再让其指向另一个对象,但是引用的值是可变的

    比如:声明一个数组,我们不能将该数组再通过等号重新赋值给另一个数组变量 但是可以修改某一下标的值

为什么局部变量内部类和匿名内部类只能访问局部final变量?

首先要知道的一点是:内部类和外部类是处于同一级别的,内部类不会因为定义在方法中就会随着方法的执行完毕就被销毁。
这里就会产生问题:当外部类的方法结束时,局部变量就会被销毁,但是内部类对象可能还存在(只是没有人再引用它时,才会死亡)。这里出现一个矛盾点:内部类对象访问了一个不存在的变量,为了解决这个问题,就将局部变量复制了一份作为内部类的成员变量,这样当局部变量死亡后,内部类仍可以访问它,实际访问的是局部变量的copy。这样就好像延长了局部变量的生命周期。
将局部变量复制为内部类的成员变量时,必须保证这两个变量是一样的,也就是如果我们在内部类中修改了成员变量,方法中的局部变量也得跟着改变,怎么解决这个问题呢?
就将局部变量设置为final。对它初始化后,我就不让你再去修改这个变量,就保证了内部类的成员变量和方法的局部变量的一致性,这实际上也是一种妥协,使得局部变量与内部类建立的拷贝保持一致。

String、StringBuffer、StringBuilder区别及使用场景

String是final修饰的,不可变的,每次操作都会产生新的String对象
StringBuffer和StringBuilder都是在原对象上操作
StringBuffer是线程安全的,StringBuilder线程不安全
StringBuffer方法都是synchronized修饰的
性能:StringBuilder > StringBuffer > String
场景:经常需要改变字符串中的内容时使用StringBuffer和StringBuilder
优先使用StringBuilder、多线程使用共享变量时使用StringBuffer(如果我们是在多线程环境下并且某个变量是共享变量时要保证数据的正确,使用StringBuffer)

重载和重写的区别

重载:发生在同一类中,方法名必须相同,参数类型、个数、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
重写:发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出异常范围小于等于父类,访问修饰符大于等于父类,如果父类方法访问修饰符为private则子类就不能重写该方法。

接口和抽象类的区别

  • 抽象类可以存在普通成员方法,而接口中只能存在public abstract 方法
  • 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的。
  • 抽象类只能继承一个、接口可以实现多个。

接口的设计目的:是对类的行为进行约束(更准确的说是一种“有”约束,因为接口不能规定类不可以有什么行为)也就是提供一种机制,可以强制要去不同的类具有相同的行为,它只约束了行为的有无,但不对如何实现进行限制。

3*0.1 == 0.3 将会返回什么?true 还是false

false,因为有浮点数不能完全精确的表示出来

能在Switch中使用String吗?

从Java7开始,我们可在Switch case中使用字符串,但这仅仅是一个语法糖,内部实现在Switch中使用字符串的hash code

对equals() 和 hashCode()的理解?

  • 为什么在重写equals方法的时候需要重写hashCode方法?

因为有强制规范指定需要同时重写hashCode方法与equals方法,许多容器、如HashMap、HashSet都依赖于hashCode与equals的规定

  • 有没有可能两个不同的对象有相同的hashCode?

有可能,两个不相等的对象可能会有相同的hashCode值,这就是为什么在hashMap中会有冲突,相等hashCode值的规定只是说如果两个对象相等,必须有相同的hashCode值,但是没有关于不相等对象的任何规定。

  • 两个相同的对象会有不同的hash Code吗?

不能,根据hashCode的规定,这是不可能的

final、finalize和finally的不同之处?

  • final是一个修饰符,可以修饰变量、方法和类,如果final修饰变量,意味着该变量的值在初始化后不能被改变
  • Java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的,但是什么时候调用了finalize()没有保证。
  • finally是一个关键字,与try和catch一起用于异常的处理。finally块一定会被执行,无论try中是否发生异常

    this() & super()在构造方法中区别?

  • 调用super()必须写在子类构造方法的第一行,否则编译不通过

  • super从子类调用父类构造,this在同一个类中调用其他构造均需要放到第一行
  • 尽管可以使用this调用一个构造器,却不能调用2个
  • thissuper不能出现同一个构造器中,否则编译不通过
  • this()super()都指的对象,不可以在static环境中使用
  • 本质this指向本对象的指针。super是一个关键字

    Java位运算符

    Java中有三种位运算符

  • <<:左移运算符,x<<1相当于x乘以2(不溢出的情况下)低位补0

  • >>:带符号运算符,x>>1相当于x除以2,正数高位补0,负数高位补1
  • >>>:无符号右移,忽略符号位,空位都以0补齐

    泛型

    为什么需要泛型?

  • 适用于多种数据类型执行的相同的代码 ```java private static int add(int a,int b){ System.out.println(a+”+”+b+”=”+(a+b)); return a+b; }

private static float add(float a,float b){ System.out.println(a+”+”+b+”=”+(a+b)); }

如果没有泛型,要实现不同类型的加法,每种类型都需要重载一个`add()`,通过泛型,我们可以复用一个方法
```java
private static <T extends Number> double add(T a,T b){
    System.out.println(a+"+"+b+"="+(a.doubleValue()+b.doubleValue()));
}
  • 泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)

    List list = new ArrayList();
    list.add("pdx");
    list.add(100);
    

    在使用上述的list中,list中的元素都是Object类型(无法约束其中的类型)所以在取出集合元素时需要人为的强制类型转换到具体的目标类型,且很容器出现java.lang.ClassCastExecption异常
    引入泛型之后,它将提供类型的约束

    List<String> list = new ArrayList<String>();
    

    泛型类如何定义

  • 从一个简单的泛型类看起 ```java class Test{ private T var; public T getVar(){

      return var;
    

    } public void setVar(T var){

      this.var = var;
    

    } }

public class TestDemo{ public static void main(String[] args){ Test t = new Test(); t.setVar(“pdx”); System.out.println(t.getVar().length()); } }


- 多元泛型
```java
class Notepad<K,V>{       // 此处指定了两个泛型类型  
    private K key ;     // 此变量的类型由外部决定  
    private V value ;   // 此变量的类型由外部决定  
    public K getKey(){  
        return this.key ;  
    }  
    public V getValue(){  
        return this.value ;  
    }  
    public void setKey(K key){  
        this.key = key ;  
    }  
    public void setValue(V value){  
        this.value = value ;  
    }  
} 
public class GenericsDemo09{  
    public static void main(String args[]){  
        Notepad<String,Integer> t = null ;        // 定义两个泛型类型的对象  
        t = new Notepad<String,Integer>() ;       // 里面的key为String,value为Integer  
        t.setKey("派大星") ;        // 设置第一个内容  
        t.setValue(22) ;            // 设置第二个内容  
        System.out.print("姓名;" + t.getKey()) ;      // 取得信息  
        System.out.print(",年龄;" + t.getValue()) ;       // 取得信息  

    }  
}

泛型接口如何定义使用?

  • 简单的泛型接口

    interface Info<T>{        // 在接口上定义泛型  
      public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型  
    }  
    class InfoImpl<T> implements Info<T>{   // 定义泛型接口的子类  
      private T var ;             // 定义属性  
      public InfoImpl(T var){     // 通过构造方法设置属性内容  
          this.setVar(var) ;    
      }  
      public void setVar(T var){  
          this.var = var ;  
      }  
      public T getVar(){  
          return this.var ;  
      }  
    } 
    public class GenericsDemo24{  
      public static void main(String arsg[]){  
          Info<String> i = null;        // 声明接口对象  
          i = new InfoImpl<String>("汤姆") ;  // 通过子类实例化对象  
          System.out.println("内容:" + i.getVar()) ;  
      }  
    }
    

    注解

    注解的作用?

    注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、方法参数、局部变量等进行注释。它的主要作用有以下四方面:

  • 生成文档,通过代码里标识的元数据生成javadoc文档

  • 编译检查,通过对代码里标识的元数据让编译器在编译期间进行检查验证
  • 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码
  • 进行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例

    注解的常见分类?

  • Java自带的标准注解,包括@Override@Deprecated@SuppressWarnings,分别用于标明重写某个方法、标明某个类或方法过时、标明要忽视的警告,用这些注解标明后编译器就会进行检查

  • 元注解,元注解是用于定义注解的注解,包括@Retention@Target@Inherited@Documented
    • @Retention用于标明注解被保留的阶段
    • @Target 用于标明注解使用的范围
    • @Inherited 用于标明注解可继承
    • @Documented用于标明是否生成javadoc文档
  • 自定义注解,可以根据自己的需求定义注解,并可用元注解对自定义注解进行注解