Java基础

Java语言学习

    • Java跨平台
    • Java语言基础部分
      • 类型转换
      • 方法重载与重写
      • API
      • Java:==
      • String和StringBuilder
    • Java面向对象
      • 继承
        • 关键字
        • 继承的好处和弊端
        • 方法调用
      • 多态
        • 多态的前提和体现
        • 多态中的调用特点
        • 多态的好处和弊端
      • 抽象类
        • 特点
      • 接口
        • 特点
        • 类和接口的关系
        • 抽象类和接口的区别
      • 内部类
        • 分类
      • Object类
        • toString方法
        • equals方法
      • 基本类型包装类
        • 自动装箱和拆箱
    • Java异常
      • JVM默认处理方案
    • JAVA集合
      • 接口
      • 一些值得注意的问题
    • Java多线程
      • 线程同步
      • 线程安全的类
      • Lock锁对象
      • 锁的种类
    • Lamda表达式
      • 省略模式
      • 注意事项
      • 和匿名内部类的区别
      • 方法引用
      • 函数式接口
    • 接口组成更新
      • 默认方法
      • 静态方法
      • 私有方法
    • Stream流
    • Java反射
      • 类加载
      • 反射机制
    • Java模块化
      • 模块服务

Java跨平台

这里的跨平台指的是跨操作系统平台。

Java通过不同的JVM将代码编译为在对应平台上可运行的文件。

Java语言基础部分

类型转换

  1. 自动类型转换:只能从小到大自动转化。
  2. 强制类型转换

方法重载与重写

重载发生在同一个类中,方法名相同,参数不同,类型不同或数量不同,且与返回值无关。

重写发生在父子类之间,为子类中出现了和父类中一样的方法声明,会对父类方法进行覆盖。

注意:重写方法访问权限不能比父类方法更低。

重写可以用@Override注解标明

API

Application Programming Interface 应用程序接口

Java:==

  1. 对于基本类型:比较值是否相同
  2. 对于对象类型:比较地址是否相同

对象如果想比较内容是否相同需要使用equals方法。

String和StringBuilder

StringBuilder是一个可变的字符串类,可以看作一个容器,内容可变,不会作为常量存在常量池当中。

Java面向对象

继承

子类会自动拥有父类的所有属性和方法。并且可以自行添加特殊的属性和方法。

父类也称基类或超类。子类也称派生类。

关键字

this关键字访问本类内容,super关键字访问父类内容。

继承的好处和弊端

  1. 好处
    1. 提高代码复用性
    2. 提高代码维护性
  2. 弊端
    1. 类与类之间的耦合度变高了

方法调用

  1. 构造方法:子类所有构造方法会默认访问父类中无参构造方法。
  2. 成员方法:优先选择子类中重写的成员方法。

多态

同一个对象,在不同时刻表现出的不同形态。

分具体类多态,抽象类多态,接口多态。

多态的前提和体现

  1. 有继承/实现关系
  2. 有方法重写
  3. 父类引用指向子类对象

多态中的调用特点

多态中方法,编译和成员变量访问要看左边引用的声明,但是调用方法要看子类的重写。

多态的好处和弊端

多态可以让我们传递父类引用,但是调用子类方法,简化代码编写,提高程序扩展性。但是不能够使用子类特有的功能。

抽象类

abstract

没有方法体的方法为抽象方法,带有抽象方法的类为抽象类

特点

  • 抽象类不能直接实例化,但是可以通过多态的方式,以子类的构造方法实例化。
  • 抽象类里可以没有抽象方法,但是有抽象方法必须声明为抽象类。
  • 抽象类的子类必须重写抽象方法,否则必须声明为抽象类

接口

interface - implements

一种公共的规范。

接口中的成员变量默认是final和static的。

特点

  • 类实现接口,需要实现接口中定义的一些规范(方法)。
  • 接口自身不能实例化,但也可以使用实现类对象实例化。
  • 接口目的是为行为做出规定,不存在构造方法。

类和接口的关系

  • 类可以多继承接口,但只能单继承类

抽象类和接口的区别

  • 抽象类本质上是类,而接口不能有构造方法等,只是一种约定规范。
  • 继承抽象类只能单继承,接口可以多继承
  • 抽象类在设计理念上主要是对类进行抽象处理,包括属性和方法,接口主要是对行为的规定与抽象。
  • 抽象类主要是定义一类对象的必要属性和方法,而接口更多的用于实现一种特殊的功能。

内部类

内部类是在一个类中定义另一个类。可以直接访问外部类的属性和方法。

外部类需要实例化内部类才能访问。

分类

  • 成员内部类:在类的成员位置,使用点操作符访问
  • 局部内部类:在类的局部位置,在方法中定义,使用
  • 匿名内部类:局部内部类的一种特殊形式。
    • 使用new关键字开头,接类或接口名
    • 是一个对象,继承了该类,重写其中的方法。

Object类

toString方法

toString方法默认返回包名+@+哈希值

建议每个子类都重写该方法。

equals方法

默认比较两个对象的地址值,与==等价。

基本类型包装类

将基本类型封装成对象便于定义更多方法操作数据。

常用操作: 用于基本数据类型和字符串之间的转换。

自动装箱和拆箱

  • 装箱:自动将基本类型转化为包装类类型(自动执行valueOf方法)
  • 拆箱:自动将包装类类型转化为基本类型(自动执行xxValue方法)
  1. 某个基础数据类型与封装类进行运算符运算时,会触发自动拆箱
  2. 调用equals方法的时候,传入基本数据类型,会自动装箱为包装类,然后比较其中的值。由于equals方法中会比较对象类型是否相同,因此如果传入不同包装类,即使值相同,也会返回false
  3. 不同类型进行==比较时,包装类会触发拆箱,同种类型比较时,会自动拆装箱。

Java异常

所有异常的超类是Throwable。

  1. Error是严重的问题,对Error的问题,一般不进行处理。
  2. Exception:
    1. 检查异常:编译期间需要检查到。
    2. RuntimeException:是运行期间可能出现的异常问题。

JVM默认处理方案

  • 异常名称,原因,位置输出到控制台
  • 停止正在运行的程序

使用try-catch自定义处理异常时,如果在try块内出现异常,则会生成异常对象,然后递交JVM进行处理。JVM会检索catch块,寻找能够捕获的异常。

JAVA集合

接口

Collection:单列集合

  • List:可重复
  • Set:不可重复

Map:双列集合

一些值得注意的问题

  • List常用的两种实现类:ArrayList和LinkList,在底层数据结构上有区别,一个是可变大小的数组,一个是链表。

  • Set

    • HashSet底层使用Map实现的,实际上是HashMap的一个实例。其中元素的唯一性通过在插入时分析对象的哈希值(根据元素的hascode值计算得到)以及equals方法,判断是否插入。因此如果用HashSet保存对象元素,必须重写hashCode和equals方法。

      HashSet的元素访问顺序是随机的,或者说与哈希函数有关。

    • LinkedHashSet使用哈希表和链表实现,迭代顺序与插入顺序一致,链表保证元素有序,哈希表保证元素唯一。

    • TreeSet能够使元素默认按照自然排序顺序迭代。也可以传递一个自定义比较器进行自定排序。

  • 哈希表 HashMap

    JDK1.8之前底层使用数组+链表实现,使用拉链法做哈希函数的冲突处理。

    JDK1.8之后做了优化,在链表长度超过一定阈值(8)之后使用红黑树(treeMap)替代链表作为底层数据结构实现,保证查找效率。

    当填充比(加载因子)超过一定阈值(默认为0.75)时,会对数组进行扩容,扩大到原来的两倍,进行再散列,这样能够缩短链表长度,增大查找效率。

  • 泛型

    • 将类型参数化,使用时传入具体的类型。
    • 将运行期间可能出现的类型异常提前到编译期间处理,并且避免了强制类型转换。
    • 使得代码复用性更高。

Java多线程

两种方法:

  1. 继承Thread,重写run方法。run方法封装执行代码,通过start方法启动线程,由JVM调用线程的run方法。

  2. 实现Runnable接口,重写run方法,以Thread类构造方法的参数传入进行运行。

    可以避免Java单继承的局限性。

线程同步

数据安全问题:多线程环境,有共享数据,是否有多条语句操作共享数据。

  1. 可以通过synchronized给代码块加锁。
    • 线程很多时,每个线程都会判断同步代码块上的锁,浪费cpu资源
  2. 给方法加synchronized关键字,锁是this对象。如果给静态方法加锁,锁默认是本类的字节码文件对象:类名.class

线程安全的类

线程安全就是看有没有加synchronized关键字,是不是同步操作。

StringBuffer、Vector、HashTable、ConcurrentHashMap(高并发,安全度高)

对于线程不安全的类,可以通过Collections中的synchronized方法将其转变为线程安全。

Lock锁对象

为了更清晰的表达如何加锁和释放锁。比synchronized有更多的锁类型,有更广泛的锁定操作。

  1. ReentrantLock:可重入锁/排他锁

    和synchronized类似,必须等待一个线程代码块执行完毕其他线程才能执行。

  2. ReadWriteLock:读写锁接口

    将读写操作分开。具体实现类有ReentrantReadWriteLock。

锁的种类

  1. 可重入锁
  2. 可中断锁:Lock是可中断锁,synchronized不是。
  3. 公平锁:尽量以请求顺序获取锁
  4. 读写锁:读写不冲突。

Lamda表达式

函数式编程思想:尽量忽略面向对象的复杂语法,强调做什么,而不是以什么形式去做。

//使用lamda表达式方式启动一个线程
new Thread(()->{
  //任务
}).start();

标准格式为:(形参)->{代码块}

省略模式

  1. 参数类型可以省略,但是要省略必须都省略
  2. 如果只有一个参数,那么()也可以省略
  3. 如果代码块语句只有一条,可以省略{};。如果有returnreturn也要省略。

注意事项

  1. 有一个接口且接口中有且仅有一个抽象方法。否则无法确定对应重写方法。
  2. 必须有上下文环境才能推导出对应接口。
    1. 根据局部变量赋值
    2. 根据调用方法参数

和匿名内部类的区别

  1. 匿名内部类可以实现任何种类的类:具体类,抽象类,接口。

    Lamda表达式实现方法必须是一个接口。

  2. 匿名内部类可以重写多个方法,Lamda表达式只能对应其中一个方法。

  3. 两者实现方式不同:

    1. 匿名内部类编译后会生成一个单独的字节码文件
    2. lamda表达式不会生成新的字节码文件,对应字节码会在运行时动态生成。

方法引用

使用已存在的方案(Lamda表达式的一种替代形式)。

会自动将参数传递给方法。

  1. 引用类的静态方法:类名::静态方法
  2. 引用对象的实例方法:对象::成员方法
  3. 引用类的实例方法:类名::成员方法——这里传参要多传递一个,第一个参数为调用方法的实例对象。
  4. 引用构造器:类名::new

函数式接口

有且仅有一个抽象方法的接口。适用于lamda表达式的接口。

使用注解@FunctionalInterface表明其为函数式接口(可选)。

如果传递参数或返回值是一个函数式接口,我们可以以Lamda表达式形式表示。

  1. supplier:生产者接口
  2. comsumer:消费者接口
  3. predicate:条件判断接口
  4. function:函数接口

接口组成更新

Java8之后加入默认方法和静态方法

Java9之后加入了私有方法

默认方法

default关键字修饰方法:解决接口升级问题,不破坏现有接口实现。

静态方法

static关键字修饰:只能被接口调用,不会被实现类继承。

私有方法

private关键字修饰:实现私有共有代码,不让外部类调用。为默认方法和静态方法提供服务。

Stream流

简化Collection操作。

通常结合Lamda表达式使用,其中传递函数式接口。

使用方法:

  1. 生成流:通过数据源(集合/数组等)例:list.stream()

    1. collection体系使用stream()
    2. map体系间接生成:可以对键、值、键值对(Entry)分别使用stream()
    3. 数组通过Stream接口的静态方法of(T... values)
  2. 中间操作:打开流,做出某种过滤或映射,然后生成新的流给下一个操作使用

    1. filter:对流中数据进行过滤,传递predicate函数式接口
    2. limit:截取前几个数据
    3. skip:跳过前几个数据
    4. concat:合并两个流(静态方法)
    5. distinct:去重
    6. sorted:进行排序
    7. map:映射成为对应元素的流
  3. 终结操作:对流进行最后的操作。

    1. forEach:访问其中每一个元素
    2. count:得到流中元素数量
  4. 收集操作:collect操作,传递参数,映射为不同类型的Collection

    传递Collectors.xxx

Java反射

类加载

对未加载到内存中的类,JVM会进行类加载、类链接、类初始化完成类的初始化。

不出意外,JVM会连续完成三个步骤,所以有时吧三个步骤统称为类加载货类初始化。

  1. 类加载:将class文件读入内存,创建一个java.lang.Class对象
  2. 类链接:检验内部结构,分配内存设置默认初始化值,将符号引用替换为直接引用
  3. 对类变量初始化

反射机制

在运行时获取一个类的变量和方法信息,然后通过获取到的信息来创建对象,调用方法。是一种动态操作,增强了程序的灵活性,使程序运行期间仍可以扩展。

要通过反射使用一个类,首先要获取该类的字节码文件对象,也就是Class类型的对象。

获取Class对象的方式:

  1. 使用类的class属性
  2. 调用对象的getClass方法
  3. 使用Class类静态方法forName(String className),传递某个类的全路径

反射可以用于越过范型检查,通过调用成员方法时传递父类Class对象实现。

反射也可以用于读取配置文件,动态的配置服务。

Java模块化

Java9完善了模块化的概念,使得Java能够更小型,更轻便的运行。并且能够分部分隐藏内容。

使用文件module-info.java文件配置模块化,使用exports导出模块,使用requires依赖模块。

模块服务

要定义一个服务接口,最后导出这个接口的一个实现类。

在模块配置文件中使用provide…with提供服务,use使用服务。

提供服务方对外提供接口,use使用服务提供的接口。

你可能感兴趣的:(面试学习记录,java,编程语言)