我是2022年双非软件工程的应届毕业生。目前,我正在准备秋季招聘,并总结了一篇好的八篇文章。如果你只是需要注意,你可以一起学习;超链接如下:
Java后端八股文(CSDN不再同步) 语雀地址
Hello, interviewer. My name is . I graduated from Zhengzhou University of Light Industry, majoring in software engineering and I will graduate next year. I have experienced a software development internship. I have done several simple projects at school and chose a personal blog project to put on my resume.OK,This is my self-introduction. I look forward to working with you in your compan
Java基础知识
锁的实现原理
面向对象
面向对象的基本概念 面对对象的流行例子是蛋炒饭和盖饭
**概论:**对象是将一组数据结构和处理方法组成对象;将具有相同行为的对象分类;
通过包装隐藏的内部细节;通过继承泛化类别;基于对象类型的动态分布通过多态实现
吃饭
- 面向过程:买菜,洗菜,做饭,洗手吃饭,洗碗
- 对象:点外卖,吃饭,扔外卖盒
:与面向过程相比,面向对象更注重事物的每一步和顺序,面向对象更注重事物的参与者(对象),以及如何更好地按需分配 对象更容易重用、扩展和维护; 直接高效地面高效
:流程使编程任务明确,实现方法和最终结果基本考虑在开发前,具体步骤清晰,便于节点分析。
高效,面向过程强调代码短小精悍,善于结合数据结构开发高效程序。
java它面向对象,但并非所有的对象都是对象,基本数据类型不是对象,所以会有封装类型
;
:明确标记允许外部使用的所有成员函数和数据项;内部细节对外部调用透明,外部调用不需要修改或关心内部实现
1.JavaBean私有属性,提供get set对外访问,因为属性赋值或获取逻辑只能由JavaBean它本身是决定的,不能由外部混乱改变
private String name; public void setName(String name){ this.name = "java 是最好的语言" name; } //该name有自己的命名规则,显然不能由外部直接赋值
2.orm框架:操作系统库,我们不需要关心如何建立连接,sql如何介绍如何执行mybatis调方法即可
:继承基本的方法,做出自己的改变和/或扩展子类的共同方法,或直接使用父类的属性,而不需要重新定义,只需要扩展自己的个性化;继承使变化中的软件系统具有一定的连续性,继承也是包装过程中可变因素的重要手段; 访问修改符决定是否可继承
根据不同类型的对象,调用相同类型的外部变量,实际执行逻辑不同
继承,方法重写,父类引用指向子类对象 .:必须有子父关系。
作用:提高了代码的和可维护性
牛客例题
父类类型 变量名 = new 子类对象 变量名.方法名()
不能直接调用子类特有功能(需要向下转换)
- 多态转型分为向上转型和向下转型
- 向上转型:多态本身就是向上转型的过程格式:父类型 变量名=new 子类型(); 适用场景:当不需要面对子类型时,可以通过提高扩展性或使用父类功能来完成相应的操作。
- 向下转型:向上转型的子类对象可以使用强制类型转换格式,将父类引用类型转换为子类引用类型: 子类类型 变量名=(子类型) 父类变量;适用场景:使用子类特有功能时。 成员变量:编译左侧,执行左侧; 成员方法:编译左侧,执行右侧。 向下转换可以调用子类型中的所有成员,但需要注意的是,如果父类引用对象指向子类对象,则在向下转换过程中是安全的,即编译不会出错。但是,如果父亲引用的对象是父亲本身,那么在向下转型的过程中是不安全的,编译也不会出错,但我们开始提到操作会出现 Java 强制类型转换异常,一般使用 instanceof 为了避免这误的操作符。
///安全向下转换是将子类对象向上转换为父类,然后将父类强制转换为子类 Animal animal = new Cat(); if (animal instanceof Cat) {
Cat cat = (Cat) new animal(); // 向下转型 ... }
instanceof 是 Java 保留关键字。它的功能是测试左边的对象是否是右边的例子,并返回 boolean 数据类型。二元运算符,左边是对象,右边是类;当对象是右类或子类创建的对象时,返回true;否则,返回false
:抽象是将一类对象的共同特征总结出来构造类的过程,包括两方面,抽象只关注对象的哪些属性和行为,并不关注这此行为的细节是什么 可以有构造函数,但不能实例化
牛客例题 牛客例题2 牛客例题3
接口
牛客例题 牛客例题2 牛客例题3 JAVA基础——接口(全网最详细教程) Java之implements
:接口被用来描述一种抽象。因为Java不像C++一样支持多继承,所以Java可以通过实现接口来弥补这个局限。 接口也被用来实现解耦。 接口被用来实现抽象,而抽象类也被用来实现抽象,为什么一定要用接口呢?接口和抽象类之间又有什么区别呢?原因是抽象类内部可能包含非final的变量,接口的静态成员变量要用static final public 来修饰 ,可以使用接口类型的引用指向一个实现了该接口的对象,并且可以调用这个接口中的方法。
可以直接把接口理解为**100%的抽象类必须全部**是抽象方法。(JDK1.8之前可以这样理解)
和抽象类区别:
- 抽象类实例化是变量指向实现抽象方法的子类对象,接口变量必须实现所有接口方法的类对象
- 抽象类要被子类继承,接口被类实现
- 接口只能做方法申明,抽象类可以做方法实现
- 接口定义的变量只能是公共的静态的常量,抽象类中是普通变量
- 接口可以通过匿名内部类实例化
牛客例题4 类实现多个接口的时候,只需要一个implements,多个接口通过逗号进行隔开,先继承类再实现接口
abstract 与 implement is a 与 like a
- 一个抽象类可以是public、private、protected、default,接口只有public;
- 一个抽象类中的方法可以是public、private、protected、default,接口中的方法只能是public和default
- abstract不能与final并列修饰同一个类;abstract 不能与private、static、final或native并列修饰同一个方法
- 抽象方法不能有方法体,抽象方法不能使用private修饰符,也不宜使用默认修饰符(default)接口 不可以实例化 。 通过接口实现类创建对象
Java抽象类、接口能否有构造方法 趣说abstract 简书 abstract牛客例题 牛客例题2 牛客例题3 牛客例题(抽象类)
抽象类的实现是为了替代繁琐的接口实现
“USES-A”关系 依赖关系,A类会用到B类,这种关系具有偶然性,临时性;在代码中的体现为:A类方法中的参数包含了B类
“HAS-A”关系 表示聚合。是整体与部分的关系,同时它们的生命周期都是一样的
“IS-A”关系 表示继承。父类与子类,一个类包含另一个类的所有属性及行为
“LIKE-A“关系 表组合
内部类
重写与重载
Java中重载与重写 牛客例题 牛客例题2
Java数据类型
Java基本数据类型 Java 基本数据类型 及 == 与 equals 方法的区别 牛客例题 牛客例题2 牛客例题3 牛客例题4
实线可以直接转,虚线直接的转换可能损失精度
位移运算符
& ( " and " ) | ( " or " ) ^ ( " xor " ) ~ ( " not " )
- 对于来说,不论是基本数据类型还是引用类型,他们都会先在栈中分配一块内存,对于基本类型来说,这块区域包含的是基本类型的内容;而对于引用类型来说,这块区域包含的是指向真正内容的指针,真正的内容被手动的分配在堆上。
- 对于来说,不论是基本数据类型还是引用类型,他们都会存储在堆内存或者方法区中;成员变量可细分为静态成员变量和普通成员变量,静态成员变量类属于类,类可以直接访问,存储在方法区中;普通成员变量属于类对象,必须声明对象之后,通过对象才能访问,存储在堆中。
基本类型的变量数据和对象的引用都是放在栈里面的,对象本身放在堆里面,显式的String常量放在常量池,String对象放在堆中
数组初始化后默认值
牛客例题 牛客例题2
牛客例题 数组的复制的效率System.arraycopy>clone>Arrays.copyOf>for循环
什么是构造方法
什么是构造方法? Java构造方法 牛客例题 牛客例题2 牛客例题3 牛客例题4
构造方法的声明:
修饰符 class_name(类名) (参数列表){
逻辑代码
}
- 构造⽅法的(包括⼤⼩写)
- 构造⽅法(连void都没有)
- 构造⽅法可以重载
- 构造⽅法不可以⼿动调⽤,只能在创建对象的时,jvm⾃动调⽤
- 构造⽅法在创建对象时只能调⽤⼀次
当⼀个类中,没有定义构造⽅法 系统会⾃动提供⼀个公开的 ⽆参的构造⽅法 当类中已经定义了构 造⽅法,系统不再提供⽆参公开构造,如果需要使⽤⽆参的构造 那么必须⾃⼰定义出来 ⼀般开发如果 定义了有参的构造 都会再定义⼀个⽆参的构造
构造方法不能被 static、final、synchronized、abstract 和 native(类似于 abstract)修饰。构造方法用于初始化一个新对象,所以用 static 修饰没有意义。构造方法不能被子类继承,所以用 final 和 abstract 修饰没有意义。
构造函数的作用是。用来创建一个对象,同时。当程序执行到new操作符时, 首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化。
泛型
泛型概述 牛客例题 牛客例题2
- 泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)
- 泛型类型在逻辑上可以看成是多个不同的类型,实际上都是相同的基本类型
泛型使用过程中操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为
Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法
泛型的主要目标是提高 Java 程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。没有泛型,这些假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中)。
泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。
泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来可能。由于泛型的实现方式,支持泛型(几乎)不需要 JVM 或类文件更改。所有工作都在编译器中完成,编译器生成类似于没有泛型(和强制类型转换)时所写的代码,只是更能确保类型安全而已。
- ? 表示不确定的 java 类型
- T (type) 表示具体的一个 java 类型
- K V (key value) 分别代表 java 键值中的 Key Value
- E (element) 代表 Element
泛型擦除后是作为Object而存在的,而基础数据类型并没有继承自Object,所以编译器不允许将基础类型声明为泛型类型。最
近版本的编译器当涉及基础类型作为泛型参数时,编译器会自动进行拆箱和装箱,所以编译器不会报错
枚举
保障类型安全,避免出现取值范围错误的问题;适合采用于信息标志和信息分类
弥补常量赋值缺陷: 1.可读性差,易用性降低; 2.类型不安全; 3.耦合性高,扩展性差
int compareTo(E o)
比较此枚举与指定对象的顺序。
Class<E> getDeclaringClass()
返回与此枚举常量的枚举类型相对应的 Class 对象。
String name()
返回此枚举常量的名称,在其枚举声明中对其进行声明。
int ordinal()
返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。
String toString()
返回枚举常量的名称,它包含在声明中。
static <T extends Enum<T>> T valueOf(Class<T> enumType, String name)
返回带指定名称的指定枚举类型的枚举常量。
static T[] values()
返回该枚举的所有值。
反射
反射小白入门 反射总结
Reflection(反射) 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,也有称作“自省”。 在程序运行状态中,,这种动态获取信息以及动态调用对象方法的功能就称为反射机制
- 可以在程序运行过程中,操作这些对象;
- 可以解耦,提高程序的可扩展性。
- 【Source源代码阶段】 Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象; 多用于配置文件,将类名定义在配置文件中,通过读取配置文件加载类。
- 【Class类对象阶段】 类名.class:通过类名的属性class获取; 多用于参数的传递
- 【Runtime运行时阶段】对象.getClass():此方法是定义在Objec类中的方法,因此所有的类都会继承此方法。 多用于对象获取字节码的方式
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的方法
牛客例题
正则表达式
简书 牛客例题
[😕/] 表示匹配 😕/ 中的任何一个字符,也就是匹配 : 或 /
[htps] 表示匹配 htps 中的任何一个字符,[htps]+ 表示一次或多次匹配前面的字符或子表达式,所以 [htps]+ 可以匹配 https
Java的位运算
JDK、JRE与JVM的关系
JDK = JRE + 开发工具
JRE = JVM + 类库
Java Development Kit 是用于开发 Java 应用程序的软件开发工具,包括了 Java 运行时的环境(JRE)、解释器(Java)、编译器(javac)、Java 归档(jar ——一种软件包文件格式)、文档生成器(Javadoc)等工具。
Java Runtime Enviroment 提供 Java 应用程序执行时所需的环境,由 Java 虚拟机(JVM)、核心类、支持文件组成。
Java Virtual Machine(Java 虚拟机)有三层含义,分别是:
- JVM规范要求
- 满足 JVM 规范要求的一种具体实现(一种计算机程序)
- 一个 JVM 运行实例,在命令提示符下编写 Java 命令以运行 Java 类时,都会创建一个 JVM 实例。
Java程序的开发过程为:
- 我们利用 JDK (调用 Java API)编写出 Java 源代码,存储于
.java
文件中 - JDK 中的编译器 javac 将 Java 源代码编译成 Java 字节码,存储于
.class
文件中 - JRE 加载、验证、执行 Java 字节码
- JVM 将字节码解析为机器码并映射到 CPU 指令集或 OS 的系统调用。
输入输出(IO)是指计算机同任何外部设备之间的数据传递。常见的输入输出设备有文件、键盘、打印机、屏幕等。数据可以按记录(或称数据块)的方式传递,也可以 流的方式传递 。
* IO流:
* 流向
* 输入流 读数据 FileReader Reader
* 输出流 写数据 FileWriter Writer
* 数据类型
* 字节流
* 字节输入流 读数据 InputStream
* 字节输出流 写数据 OutputStream
* 字符流
* 字符输入流 读数据 Reader
* 字符输出流 写数据 Writer
字节流传输需要刷新缓冲区,字符流无需刷新缓冲区,直接转换
- 二进制文件只能使用字节流进行复制
- 文本文件可以用字节流也可以用字符流进行复制
InputStream |-- FileInputStream (基本文件流) |-- BufferedInputStream |-- DataInputStream
|-- ObjectInputStream
Reader |-- InputStreamReader (byte->char 桥梁) |-- BufferedReader (常用) Writer |-- OutputStreamWriter (char->byte 桥梁) |-- BufferedWriter |-- PrintWriter (常用)
牛客例题 处理Unicode字符
面向字符的流
牛客例题 牛客例题2 牛客例题3 牛客例题4
BIO NIO AIO
JAVA中BIO、NIO、AIO的分析理解 什么是BIO、NIO和AIO?
- BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
- NIO:New IO(或 None Blocking IO)同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
- AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非阻塞IO ,异步 IO 的操作基于事件和回调机制。
适用场景
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO方式适用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
NIO 比 BIO 把一些无效的连接挡在了启动线程之前,减少了这部分资源的浪费。因为我们都知道每创建一个线程,就要为这个线程分配一定的内存空间。``AIO 比 NIO 进一步改善是,将一些暂时可能无效的请求挡在了启动线程之前,比如在 NIO 的处理方式中,当一个请求来的话,开启线程进行处理,但这个请求所需要的资源还没有就绪,此时必须等待后端的应用资源,这时线程就被阻塞了。
——————————————————————————————————————————————
以下了解即可,
同步和异步
- 同步——指的是用户进程触发 IO 操作并等待或者轮询的去查看 IO 操作是否就绪。
- 异步——异步是指用户进程触发IO操作以后便开始做自己的事情,而当 IO 操作已经完成的时候会得到 IO 完成的通知(异步的特点就是通知)
:O 操作主要分为两个步骤,即发起 IO 请求和实际 IO 操作,同步与异步的区别就在于第二个步骤是否阻塞。若实际 IO 操作阻塞请求进程,即请求进程需要等待或者轮询查看 IO 操作是否就绪,则为同步 IO;若实际 IO 操作并不阻塞请求进程,而是由操作系统来进行实际 IO 操作并将结果返回,则为异步 IO。
阻塞与非阻塞
- 阻塞——所谓阻塞方式就是指,当视图对文件描述符或者网络套接字进行读写时,如果当时没有东西可读,或者暂时不可写,程序就进入等待状态,直到有东西读或者写
- 非阻塞——所谓的非阻塞方式就是指,当视图对文件描述符或者网络套接字进行读写时,如果没有东西可读,或者不可写,读写函数马上返回,无须等待
**区别:**O 操作主要分为两个步骤,即发起 IO 请求和实际 IO 操作,阻塞与非阻塞的区别就在于第一个步骤是否阻塞。若发起 IO 请求后请求线程一直等待实际 IO 操作完成,则为阻塞 IO;若发起 IO 请求后请求线程返回而不会一直等待,即为非阻塞 IO。
阻塞和非阻塞是针对于进程在访问数据的时候,根据 IO 操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入函数会立即返回一个状态值。
同步阻塞与同步非阻塞
- 同步并阻塞 IO——服务器实现模式一个连接一个线程,即客户端有连接请求时服务端就需要启动一个线程进行处理,如果这个连接不做任何事情,就会造成不必要的线程开销,当然可以通过线程池(Thread-Pool)程机制改善
- 同步非阻塞——同步非阻塞,服务器实现模式一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有 I/O 请求时才启动一个线程处理。用户进程也需要时不时地询问IO操作是否就绪,这就要求用户进程不停的去询问。
异步阻塞与异步非阻塞
- 异步阻塞——应用发起一个 IO 操作以后,不需要等待内核 IO 操作完成,等待内核完成 IO 操作以后会通知应用程序,这其实就是异步和同步的关键区别,同步必须等待或者主动去询问 IO 操作是否完成。那为什么说阻塞呢?因为此时是通过 select 系统调用来完成的,而 select 函数本身的实现方式就是阻塞的,但采用 select 函数有个好处就是它可以同时监听多个文件句柄(如果从UNP的角度看,select 属于同步操作。因为 select 之后,进程还需要读写数据),从而提高系统的并发性。
- 异步非阻塞——此种方式下,用户进程只需要发起一个IO操作便立即返回,等 IO 操作真正完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据处理就好了,不需要进行实际的 IO 读写操作,因为真正的 IO 操作已经由操作系统内核完成了。
Java体系结构包括四个独立但相关的技术
- Java程序设计语言
- Java.class文件格式
- Java应用编程接口(API)
- Java虚拟机
我们再在看一下它们四者的关系:
当我们编写并运行一个Java程序时,就同时运用了这四种技术,用编写源代码,把它编译成,然后再在。当程序运行的时候,它通过调用class文件实现了来满足程序的Java API调用
牛客例题
关键字
对象的初始化方法:
new初始化 静态工厂 newInstance() 反射Class.forname() clone() 反序列化
其中new初始化和反射用到了构造方法,静态的newInstance()只能调用无参构造器
牛客例题
abstract continue for new
switch default if package
synchronized do goto private
this break double implements
protected throw byte else
import public throws case
enum instanceof return transient
catch extends int short
try char final interface
static void class finally
long strictfp volatile const
float native super while
boolean assert
标识符
牛客例题
- 1.不能数字开头 2.标识符用$,_,字母,数字组成 3.不能用java关键字,保留字(关键字都是小写的)
- 4.不能用true,false,null来定义标识符 5.java大小写敏感 6.没有长度限制
权限修饰符
Java中各类修饰符 牛客例题 牛客例题2

for 与 foreach
foreach适用于只是进行集合或数组遍历,for则在较复杂的循环中效率更高。
foreach不能对数组或集合进行修改(添加删除操作),如果想要修改就要用for循环。 所以相比较下来for循环更为灵活。
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
for (int i = 0; i < 10; i++) {
set.add(i);
System.out.print(set.remove(i) + "- ");
}
System.out.print("\n" + set.toString());
}
- modCount是ArrayList中的一个成员变量,它表示该集合实际被修改的次数。
- expectedModCount是ArrayList中的一个内部类 Itr中的成员变量
- expectedModCount表示这个迭代器期望该集合被修改的次数,其值是在ArrayList.iterator方法被调用的时候初始化的,只有通过迭代器对集合进行操作,该值才会改变。
- Itr是一个Iterator的实现,使用ArrayList.iterator方法可以获取到的迭代器就是Itr类的实例
直接调用ArrayList的add/remove方法,都会改变modCount的值,但并没有对expectedModCount做任何操作
单线程场景,Java8之前的代码中建议使用Iterator进行元素删除/添加;Java8之后可以考虑使用Stream及filter
:对类进行初始化
结论:
原因:
- 一、接口可以理解为“完全抽象类”,只包含常量和抽象方法;接口中的方法默认被 public 、abstract 修饰,不能有方法体,所以接口中不能有构造方法。 (弱的所属关系is-a)
- 二、抽象类可以有构造方法 (强的所属关系is-a)
- 抽象类中可以有抽象方法和普通方法,不能使用new创建它的实例;含有抽象方法的类必须声明为抽象类,抽象类扩展的非抽象子类中,必须实现所有的抽象方法
- 普通方法可以有方法体,构造方法是没有返回值的方法,在new实例化对象时被调用。所以抽象类可以有构造方法。
一个子类只能继承一个父类,但可以实现任意个数的接口,某种程度上接口比抽象类更加灵活
使用static声明的内部抽象类相当于一个外部抽象类,继承的时候使用“外部类.内部类”的形式表示类名称。
abstract class A{
//static定义的内部类属于外部类
static abstract class B{
public abstract void print();
}
}
class C extends A.B{
public void print(){
System.out.println("**********");
}
}
public class TestDemo {
public static void main(String[] args) {
//向上转型
A.B ab = new C();
ab.print();
}
}
switch
byte short int char 枚举 在JDK1.7版本以后,Switch语句也可以接string类型
final
浅谈Java中的final关键字 java中的Static、final、Static final各种用法 牛客例题
可用来修饰类,方法,变量
1.final修饰变量,则等同于常量 2.final修饰方法中的参数,称为最终参数。
3.final修饰类,则类不能被继承 (太监类)
4.final修饰方法,则方法不能被重写。5.final 不能修饰抽象类
6.final修饰的方法可以被重载 但不能被重写 (不能继承)
- 当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。在使用final修饰类的时候,要注意谨慎选择,,尽量不要将类设计为final类
- 使用final方法的原因有两个:第一个原因是把,以防任何继承类修改它的含义;第二个原因是。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。 只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。 注:
- 对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
static作用于成员变量用来表示只保存一份副本,而final的作用是用来保证变量不可变
即使没有final修饰的情况下,在方法内部改变了变量i的值也不会影响方法外的i;java采用的是值传递,对于引用变量,传递的是引用的值,也就是说让实参和形参同时指向了同一个对象,因此让形参重新指向另一个对象对实参并没有任何影响
static
深入理解static关键字
static修饰的变量没有发生变化是因为static作用于成员变量只是用来表示保存一份副本,其不会发生变化。怎么理解这个副本呢?其实static修饰的在类加载的时候就加载完成了(初始化),而且只会加载一次也就是说初始化一次,所以不会发生变化!
**static是不允许用来修饰局部变量 ** 静态变量只能在类主体中定义,不能在方法中定义
- 静态变量: 静态变量由于不属于任何实例对象,属于类的,所以在内存中只会有一份,在类的加载过程中JVM只为静态变量分配一次内存空间。
- 实例变量: 每次创建对象都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就有几份成员变量。
牛客例题 静态方法中不能调用对象的变量,因为静态方法在类加载时就初始化,对象变量需要在新建对象后才能使用
牛客例题2 静态变量和静态块的初始化顺序是靠他们俩的位置决定的
牛客例题3 static成员函数既可以通过类名直接调用,也可以通过对象名进行调用
牛客例题4 静态变量只能在类主体中定义,不能在方法中定义
牛客例题5 被动引用不会出发子类初始化 1.子类引用父类的静态字段,只会触发子类的加载、父类的初始化,不会导致子类初始化 2.通过数组定义来引用类,不会触发此类的初始化 3.常量在编译阶段会进行常量优化,将常量存入调用类的常量池中, 本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化。
牛客例题6 静态变量会默认赋初值,局部变量和final声明的变量必须手动赋初值
修饰符大汇总
super
super的作用 牛客例题
- 调用父类被子类重写的方法;
- 调用父类被子类重定义的字段(被隐藏的成员变量);
- 调用父类的构造方法;
如果子类没有重写父类的方法,调用父类的方法用不用super
关键字结果都一样。 如果子类重写父类的方法,调用父类的方法必须用super
关键字
super关键字详解
this与super
牛客例题
- 。this也,this也,不能调用static静态成员变量
- 。super也,super也。super 只是代表当前对象内部的那一块父类型的特征。
- this的作用其中一个就是在一个构造方法中调用另一个构造方法,格式为this(参数);
super可以访问父类中public、default、protected修饰的成员变量,实例方法,构造方法格式如下
super.属性名 【访问父类的属性】
super.方法名(实参) 【访问父类的方法】
super(实参) 【调用父类的构造方法】
super()在无参构造的使用
为什么要有无参构造
在子类的构造方法中编译器会自动在子类构造函数的第一句加上 super(); 来调用父类的无参构造器;此时可以省略不写。如果想写上的话必须在子类构造函数的第一句,可以通过super来调用父类其他重载的构造方法,只要相应的把参数传过去就好 牛客例题
String、StringBuffer、StringBuilder
String、StringBuffer与StringBuilder之间区别 牛客例题 牛客例题2
- String由final修饰,是不可变的,每次操作都会产生新的String对象
- StringBuffer和StringBuilder都是在原对象上操作的,可改变 (常量池可追加字符串)
- StringBuffer线程安全,StringBuilder线程不安全;StringBuffer方法都是synchronized修饰
性能:StringBuilder > StringBuffer > String
场景:经常需要改变字符串内容时使用后面两个;单线程环境下优先使用StringBuilder,多线程使用共享变量时使用StringBuffer;
new String 与String
java堆、栈、堆栈,常量池的区别 String不new和new的区别 Java的String类常量池 图解 牛客例题 牛客例题2 牛客例题3 牛客例题4
,不能存对象,而。 ==是比较地址,equals()比较对象内容。
,如果常量池中有“ABC”,则不创建对象,直接指向那个地址;如果”ABC”这个字符串在java 常量池里不存在,会在常量池里创建一个创建一个String对象(“ABC”),然后str1指向这个内存地址,无论以后用这种方式创建多少个值为”ABC”的字符串对象,始终只有一个内存地址被分配,之后的都是String的拷贝,Java中称为“字符串驻留”,所有的字符串常量都会在编译之后自动地驻留。
。如果常量池有“ABC”这个字符串,new关键字意味着将在heap中创建一个str2的String对象,value引用至“ABC”。如果这个字符串(ABC)在java 常量池里不存在,还会在常量池里创建这个String对象“ABC”(创建了第二个对象),即创建了两个对象。
栈与堆的区别
程序内存布局场景下,堆与栈表示两种内存管理方式:
(1)管理方式不同。栈由操作系统自动分配释放,无需我们手动控制;堆的申请和释放工作由程序员控制,容易产生内存泄漏;
(2)空间大小不同。每个进程拥有的栈的大小要远远小于堆的大小。理论上,程序员可申请的堆大小为虚拟内存的大小,进程栈的大小 64bits 的 Windows 默认 1MB,64bits 的 Linux 默认 10MB;
(3)生长方向不同。堆的生长方向向上,内存地址由低到高;栈的生长方向向下,内存地址由高到低。
(4)分配方式不同。堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是由操作系统完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由操作系统进行释放,无需我们手工实现。
(5)分配效率不同。栈由操作系统自动分配,会在硬件层级对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是由C/C++提供的库函数或运算符来完成申请与管理,实现机制较为复杂,频繁的内存申请容易产生内存碎片。显然,堆的效率比栈要低得多。
(6)存放内容不同。栈存放的内容,函数返回地址、相关参数、局部变量和寄存器内容等。当主函数调用另外一个函数的时候,要对当前函数执行断点进行保存,需要使用栈来实现,首先入栈的是主函数下一条语句的地址,即扩展指针寄存器的内容(EIP),然后是当前栈帧的底部地址,即扩展基址指针寄存器内容(EBP),再然后是被调函数的实参等,一般情况下是按照从右向左的顺序入栈,之后是被调函数的局部变量,注意静态变量是存放在数据段或者BSS段,是不入栈的。出栈的顺序正好相反,最终栈顶指向主函数下一条语句的地址,主程序又从该地址开始执行。堆,一般情况堆顶使用一个字节的空间来存放堆的大小,而堆中具体存放内容是由程序员来填充的。
==和equals比较
- ==对比的是栈中的值,基本数据类型是变量值,引用数据类型是堆中内存对象的地址; 直接比较的两个对象的堆内存地址,如果相等,则说明这两个引用实际是指向同一个对象地址的
- equals:object中默认也是采用==比较,通常会重写
== 是java提供的等于比较运算符,用来比较两个变量指向的内存地址是否相同.而equals()是Object提供的一个方法.Object中equals()方法的默认实现就是返回两个对象==的比较结果.但是equals()可以被重写,所以我们在具体使用的时候需要关注equals()方法有没有被重写. ==和equals 牛客例题 牛客例题
instanceof运算符
牛客例题
用来判断,instanceof
左边对象是否为instanceof
右边类的实例,返回一个boolean类型值。还可以用来判断子父类的所属关系。
//测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据
String s = "I AM an Object!";
boolean isObject = s instanceof Object;
**应用场景:**需要用到对象的强制类型转换时,需要使用instanceof进行判断。
可以判断 一个类的实例 一个子类的实例 一个实现指定接口的类的实例
instanceof详解
Scanner,BufferedReader,InputStreamReader 简介与对比
:Scanner类是读取并转换输入流的,而BufferedReader类是直接读取输入流,并不做转换;由此BufferedReader类读取的速度要比Scanner类读取的速度快;由于BufferedReader类读取输入流不进行转换,Scanner类也可以通过一个BufferedReader对象来实例化;
函数取值方法
牛客例题
- 大数取整(四舍五入) Math.round 取最接近整数,如果遇到一样近,则取最大值 Math.round(10.5) = 11 Math.rond(-10.5) = -10
- 向上取整 Math.ceil 无脑进位(向大方向) Math.ceil(10.1) = 11 Math.ceil(-9.6)= 9
- 向下取整 Math.floor 无脑退位(向负方向) Math.floor(10.6) = 10 Math.floor(-9.2)=-10
集合
牛客例题
线程安全的类有hashtable concurrentHashMap synchronizedMap
总共有两大接口:Collection 和Map ,一个元素集合,一个是键值对集合;
链接:https://www.nowcoder.com/questionTerminal/709828a56a4443d899d89ae95b7c2940 来源:牛客网
java.util.Collection [I]
Collection 接口常用的方法
牛客例题
方法名称 | 说明 |
---|---|
boolean add(E e) | 向集合中添加一个元素,如果集合对象被添加操作改变了,则返回 true。E 是元素的数据类型 |
boolean addAll(Collection c) | 向集合中添加集合 c 中的所有元素,如果集合对象被添加操作改变了,则返回 true。 |
void clear() | 清除集合中的所有元素,将集合长度变为 0。 |
boolean contains(Object o) | 判断集合中是否存在指定元素 |
boolean containsAll(Collection c) | 判断集合中是否包含集合 c 中的所有元素 |
boolean isEmpty() | 判断集合是否为空 |
Iteratoriterator() | 返回一个 Iterator 对象,用于遍历集合中的元素 |
boolean remove(Object o) | 从集合中删除一个指定元素,当集合中包含了一个或多个元素 o 时,该方法只删除第一个符合条件的元素,该方法将返回 true。 |
boolean removeAll(Collection c) | 从集合中删除所有在集合 c 中出现的元素(相当 |