java面试全解
时间:2023-02-19 00:30:00
文章目录
- 面试前的工作
-
- 自我介绍
- 知识储备充足
- javaSE
-
- JMM内存模型
- jdk1.8新特性
- java语言的特征
- 如何实现跨平台
- 谈谈你对面向对象的理解
- 聊聊面向对象的特征,封装,继承,多态
- 访问权限
- 对象创建过程 从jvm从角度出发,
- 延伸到对象内存中的存储空间
- Java类初始化顺序
- java创建对象的方式
- 对象克隆、浅克隆、深克隆
- 构造方法
- 对象与引用
- 静态static
- 抽象与接口
- Object
- 判断两个对象是否相等
- String
-
- 特征
- 为什么不变?
- String类不可变性的好处 为什么设计是不可?.
- String值能改变吗?
- String创建对象的方法
- 创建了多少对象?
- string常用的方法
- String,StringBuffer,StringBuilder区别
- 包装基本类型
- 异常
-
- 什么是异常?
- 异常体系
- 异常分类
- 常见的异常类有哪些?
- 异常处理
- 自定义异常
- 集合
-
- 为什么需要集合?
- 集合体系
- 实现各集合类底层结构
- **HashMap结构**
- 红黑树
- AVL树
- hashmap和hashtable区别
- 泛型
- IO 延伸到操作系统 NIO
-
- File类
- 流的类型
- 节点流
- 处理流
- 对象序列化、反序列化
- 线程
-
- 最近最近没用lru
- 线程、过程名词解释、关系
- 如何创建线程
- 状态
- 多线程优缺点
- 如何解决线程安全问题:并发编程
-
- 并发编程的核心问题:
- 如何保证原子性?
- CAS
- 如何避免死锁?.
- 守护线程
- 线程间通信
- 线程面试题
-
- join方法
- sleep()方法和wait()方法差异
- Thread和Runnable关系,差异
- 锁优化,如何优化?
- 锁状态
- 锁分类
-
- synchronized和ReentrantLock(Lock的实现类)
- AtomicInteger底层实现的原理是什么? 包证原子性
- ThreadLocal的底层原理
-
- 线程封闭
- 概述
- 原理分析
- 对象引用
- 内存泄漏问题
-
- 解决方法:
- synchronized 和 volatile 有什么区别?
-
- volatile关键词的作用
- synchronized作用
- 区别
- synchronized底层
- 线程的 run() 和 start() 有什么区别?
- Runnable和Callable的区别 T call()Throws Exception{}
- 什么是CAS
- 什么是AQS
- 线程池
-
-
- ThreadPoolExector类
- 构造器中各参数的含义:
- 设置合理的线程池大小
-
- java反射
-
- 反射机制的应用场景
- 反射机制的优缺点
- 注解
-
-
- 内置的注解
- 自定义注释
-
- 面向对象的设计原则
- 设计模式
-
-
- 常用的设计模式
-
- 单例模式(创新模式)
- 工厂模式(创新模式)
- 代理模式(结构模式)
- 装饰
- 观察者
- 建造者
-
- 网络
-
-
- OSI 七层模型有哪些?
- TCP与UDP区别
- 三次握手是什么?
- 为什么不能两次?
- 四次挥手是什么?
- TCP如何保证有序传输?
- TCP如何保证流量控制传输?
- TCP如何保证拥塞控制传输?
- TCP如何保证可靠传输?
- dns是什么?dns的工作原理
- Cookie和session区别
- GET 和 POST 的区别
- TCP/IP
- HTTPS和HTTP的区别
- 什么是SSL ?秘钥
- HTTP 请求响应过程
- 一次完整的HTTP请求过程
- 详解 HTTP 报文
- session 工作原理?
- http长连接和短连接的区别
- 什么是CSRF攻击
- 常见的状态码有哪些?
- 防范常见的 Web 攻击
- 解决跨域问题
- 密码加密机制
-
- javaEE
-
-
- jsp和Servlet的区别和联系
- servlet
- servlet作用
- 生命周期
- servlet是线程安全的吗
- 拦截器
- 过滤器
-
- 我们不一样
-
- 框架
-
- mybatis
-
- **什么是Mybatis?**
- **Mybaits的优缺点**
- **简述Mybatis的运行流程**
- **#{}和${}的区别是什么?**
- MyBatits的resultType和resultmap的区别?
- 使用 MyBatis 的 mapper 接口调用时有哪些要求
- **什么是MyBatis的接口绑定?**
- **Mybatis动态sql是做什么的?都有哪些动态sql?能简述一下动态sql的执行原理不?**
- **Mybatis是否支持延迟加载?如果支持,它的实现原理是什么?**
- Mybatis的一级、二级缓存
- spring
-
- 什么是spring
- spring构成模块
- 优点
- 缺点
-
- 如何避免?
- IOC
-
- 依赖注入(DI)
- 依赖注入方式 xml 注解
- 注解的工作原理
- @Autowired 与@Resource的区别
- AOP
- spring事务管理
- 说一下 spring 的事务传播行为
- **BeanFactory和ApplicationContext**
- **springBean的生命周期**
- Spring 中的bean 是线程安全的吗?
- Spring的Bean作用域类型
- Spring懒加载
- **Bean**循环依赖
-
- Spring内部三级缓存:
- Spring MVC 运行流程
-
- 1.概述
- 2.SpringMVC运行流程
- Spring MVC的常用注解解释
- SpringMVC中如何解决POST请求中文乱码问题,GET请求的又如何处理呢?
- Servlet的过滤器与Spring拦截器详解
- springBoot
-
-
- 什么是 Spring Boot?
- Spring Boot 有哪些优点?
- Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?
- 什么是 JavaConfig?
- Spring Boot 自动配置原理是什么?
- SpringBoot的start
- 你如何理解 Spring Boot 配置加载顺序?
- 什么是 YAML?
-
- YAML 配置的优势在哪里 ?
- 什么是 Spring Profiles?
-
- 如何在自定义端口上运行 Spring Boot 应用程序?
- 如何实现 Spring Boot 应用程序的安全性?
- 比较一下 Spring Security 和 Shiro 各自的优缺点 ?
- 什么是 CSRF 攻击?
- Spring Boot 中的监视器是什么?
-
- 如何在 Spring Boot 中禁用 Actuator 端点安全性?
- 前后端分离,如何维护接口文档 ?
- spring-boot-starter-parent 有什么用 ?
- 运行 Spring Boot 有哪几种方式?
-
- Spring Boot 需要独立的容器运行吗?
- 开启 Spring Boot 特性有哪几种方式?
- 如何使用 Spring Boot 实现异常处理?
- 如何使用 Spring Boot 实现分页和排序?
- 微服务中如何实现 session 共享 ?
- Spring Boot 中如何实现定时任务 ?
- Spring缺点
- 什么是springboot
- 3.SpringBoot特点
- SpringBoot注解
- @Component和@Bean的区别
-
- JWT令牌
-
-
- 1.起源
- 2.JWT的主要应用场景
-
- JVM
-
- 概述
-
- 1.虚拟机
- 2.JVM作用
- 3.JVM的位置
- 4.JVM整体组成
- 5.java代码的执行流程
- 6.JVM架构模型
- 类加载
-
- 1.类加载子系统的作用
- 2.类加载过程
-
-
- 1.加载
- 2.链接
- 3.初始化
-
- 3.类加载器分类
- 4.类加载机制JVM的类加载机制主要有3种
- 5.沙箱安全机制
- 6.类的主动使用/被动使用
- 7.类装载方式
- 运行时数据区
-
- 1.概述
- 2.程序计数器(Program Counter Register)
- 3.虚拟机栈(Java Virtual Machine Stack)
- 4.本地方法栈(Native Method Stack)
- 5.Java堆内存
- 6.方法区
- 7.常见面试题
- JVM执行引擎
-
- 概述
- Java代码编译和执行过程
- 什么是解释器?什么是JIT编译器?
- 为什么要保存解释器?
- 编译器不确定性
- 为什么Java是半编译半解释语言?
- 什么是热点代码?
- JIT编译器执行效率高为什么还需要解释器?
- JVM本地方法接口
-
- 1.什么是本地方法
- 2.为什么使用Native Method
- JVM-垃圾回收
-
-
- 1.垃圾回收概述
-
- 1.概述
- 2.什么是垃圾?
- 3.Java垃圾回收机制
- 2.垃圾回收相关算法
-
- 1.垃圾标记阶段算法
- 2.垃圾回收阶段算法
- 3.标记-压缩算法
- 总结
- 4.分代收集算法
- 5.增量收集算法
- 6.分区算法
- 3.垃圾回收相关概念
-
- 1.System.gc()
- 2.内存溢出与内存泄漏
- 3.Stop The World
- 4.对象的引用
- 4.垃圾回收器
-
- 1.GC性能指标
- 2.HotSpot垃圾收集器
-
- JVM性能调优参数
-
- JVM内存模型/JMM
-
- JMM同步规定
- 内存溢出,内存泄漏,哪些区域会出现内存溢出OOM
- 说说Java栈和堆
- 哪些区域是线程私有,哪些区域是线程共享
- JVM 年轻代到年老代的晋升过程的判断条件是什么
- 各种回收器,各自优缺点,重点CMS、G1
- final finally finalize()区别
- MySQL数据库
-
- mysql如何做分库分表的?
- 基本操作
- 引擎
-
- 1.概述
- 2.InnoDB与MyISAM
- 索引
-
- 索引失效的情况:
- 1.为什么使用索引
- 2.优点
- 3.缺点
- 4.索引分类
- 5.索引创建原则
-
- 哪些情况需要创建索引?
- 哪些情况不需要创建索引?
- 6.索引数据结构(B树)
- 7.聚簇索引和非聚簇索引(辅助索引)
- 事务
-
- 1.概述
- 2.事务特性
- 原子性和持久性是怎么保证的?
- 3.事务设置
-
- 并发事务处理带来的问题
- 4.事务隔离级别
- 锁
-
- 1.概述
- 2.行锁与表锁
-
- 行锁
- 表锁:
- 底层机制MVCC
-
- 2、当前读
- 3、快照读(提高数据库的并发查询能力)
- 4、当前读、快照读、MVCC关系
-
- 基本原理
- 视图
-
-
- 1.概述
- 2.为什么使用
- 3.语法
-
- 存储过程
-
-
- 1.概述
- 2.语法
-
- 函数
-
-
- 1.语法
- 不带参数
- 带参数
- 有参数,有判断
-
- 触发器
-
-
- 1.概述
- 2.特点
- 3.语法
-
- SQL优化
-
-
- 方法
- 执行计划
- EXPLAIN关键字
-
- mysql面试题
- sql join原理?
-
- innodb为什么要用自增id作为主键
- 数据库三范式
-
- 第一范式:列不可再分
- 第二范式属性完全依赖于主键
- 第三范式属性不依赖与其他非主键,属性直接依赖与主键
- 如何做 MySQL 的性能优化
- CHAR和VARCHAR和TEXT的区别?
- NOW()和CURRENT_DATE()有什么区别?
- 说一说drop、delete与truncate的区别
- redis概述
-
-
- 持久化机制
- 数据一致性
-
- 解决方法
- 1.是什么?
- 2.特点
- 3.优势
- 4.Redis是单进程单线程的?
- 5.关系型数据库与非关系性数据库
- 6.Redis中过期的key是怎么被删除的?
-
- 2.数据类型
-
-
- 1.string(字符串)
- 2.hash(哈希)
- 3.list(列表)
- 4.set(集合)
- 5.zset(有序集合)
- 6.设置失效时间
-
- 3.springboot集成使用redis
- 4.主从复制
- 5.哨兵机制
-
-
-
- 1、哨兵的介绍
- 2、哨兵的核心知识
- 3、为什么redis哨兵集群只有2个节点无法正常工作?
- 4、经典的3节点哨兵集群
- 5、redis两种数据丢失的情况
- 6、解决异步复制和脑裂导致的数据丢失
- 7、哨兵的自动切换机制
-
-
- 6.缓存穿透、缓存击穿、缓存雪崩
-
-
- 1.缓存处理流程
- 2.缓存穿透
-
- 布隆过滤器
- 3.缓存击穿
- 4.缓存雪崩
-
- nginx
-
- 概述
-
- 1.Nginx是什么?
- 2.特点
- 3.代理服务器
- 4.正向代理
- 5.反向代理
- 6.负载均衡
- 7.动静分离
- Linux
- maven
-
-
- 1.概述
- 2.**maven** **命令**
- 3.Maven父子工程依赖jar传递方式
-
- Git
-
-
- 什么是版本控制
- 1.概念
-
- 集中式版本控制
- 分布式版本控制
- 2.GIT基本操作
-
- 项目
-
- 需求的诞生
- 需求分析
- 使用到的技术
- 任务分工
- 如何沟通
- 你的职责
- 如何解决问题
面试前的工作
自我介绍
面试官好:
很荣幸有机会参加xxx公司面试。我此次应聘的职位是*****。下面我请允许我自我介绍一下。
我叫***,今年***,来自******,目前是********大四学生,对软件开发怀有浓烈的兴趣,对Java语言尤其熟悉,有良好的Java编程知识和面向对象编程思想,熟悉常用的数据结构和算法,熟悉 MVC 开发模式和前后端分离框架,熟练使用 Maven、Git 管理工具,熟悉 Web 框架和 J2EE 开发技术,熟悉 Linux 操作系统和常用的操作指令、熟悉 JVM 内存模型、类加载 。目前已经编写了几个项目,拥有一定的项目经验,获奖情况 。
我觉得自己为人踏实、勤奋、好学,严格要求自己,工作积极,一丝不苟,对新事物有较强的接受能力。对自己已掌握的技术敢于创新,不畏难题,勇于迎接新挑战!在平时生活中喜欢将自己学到的一些新技术写成博客与他人分享.也会经常阅读他人的技术性文章.来提升自己的能力,丰富自己的想法。能够妥善的处理周围的人际关系,团结同事,并具有极强的团队合作精神。我能够通过自己的努力在为企业服务的过程中实现自身价值。
专业书籍:深入理解JVM、鸟哥的Linux私房菜、大话设计模式
充足的知识储备
操作系统
计算机网络
数据结构算法
java语言(javaSE,javaEE,框架,JVM)
数据库
项目
linux
javaSE
javaSE内容作为java开发方向的基础知识,一定要扎实,对知识点要有自己深入的理解.一旦有磕绊对面试官的印象将大打折扣.
JMM内存模型
Java内存模型规定了所有的变量都存储在主内存中。每条线程还有自己的工作内存,线程的工作内存中保存了被该线程使用的变量的主内存副本,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的数据。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。
jdk1.8新特性
-
Lambda表达式
本质上是一段匿名内部类,也可以是一段可以传递的代码。
-
函数式接口
定义了一个抽象方法的接口,就是函数式接口,并且还提供了注解:@Functionallnterface
-
*方法引用和构造器调用
-
Stream API
-
接口中的默认方法和静态方法
在接口中可以使用default和static关键字来修饰接口中定义的普通方法
在JDK1.8中很多接口会新增方法,为了保证1.8向下兼容,1.7版本中的接口实现类不用每个都重新实现新添加的接口方法,引入了default默认实现,static的用法是直接用接口名去调方法即可。当一个类继承父类又实现接口时,若后两者方法名相同,则优先继承父类中的同名方法,即“类优先”,如果实现两个同名方法的接口,则要求实现类必须手动声明默认实现哪个接口中的方法。
-
新时间日期API
新的日期API都是不可变的,更使用于多线程的使用环境中。
java语言的特征
简单性、面向对象、分布式、健壮性、安全性、体系结构中立、可移植性、跨平台性、解释型、高性能、多线程、动态性。
不要小看此问题,他会引出一连串问题:
例如:如何实现跨平台,JVM作用,面向对象的理解,多线程…
如何实现跨平台
为不同的平台的提供不同的JVM(虚拟机),将字节码翻译/编译成不同平台支持的指令.
JDK:Java开发工具包 包含jre
JRE:Java运行环境(核心工具类) 包含JVM
JVM:Java虚拟机
谈谈你对面向对象的认识理解
面向过程分析出解决问题所需的步骤,然后将步骤一步步实现。
比如将大象装进冰箱,分为三步,打开冰箱,放入大象,关上冰箱。
面向过程以分类的方法进行思考和解决问题,面向对象的思维方式适合于处理复杂的问题。面向对象无法取代面向过程,两者是相辅相成的,面向对象关注于宏观上把握事物之间的关系,在具体到如何实现某个细节时,任然采用面向过程的思维方式。
比如将大象装进冰箱,首先设计三个类冰箱类(门类(包含开门,关门两个方法)),人类(包含操作方法),大象类(进入冰箱功能)
接着按照面向过程的思想调用三个类,解决问题。
类的构成{
成员变量
成员方法
构造方法
代码块
内部类
}
1、易维护
采用面向对象思想设计的结构,可读性高,由于继承的存在,即使改变需求,那么维护也只是在局部模块,所以维护起来是非常方便和较低成本的。
2、质量高
在设计时,可重用现有的,在以前的项目的领域中已被测试过的类使系统满足业务需求并具有较高的质量。
3、效率高
在软件开发时,根据设计的需要对现实世界的事物进行抽象,产生类。使用这样的方法解决问题,接近于日常生活和自然的思考方式,势必提高软件开发的效率和质量。
4、易扩展
由于继承、封装、多态的特性,自然设计出高内聚、低耦合的系统结构,使得系统更灵活、更容易扩展,而且成本较低
聊聊面向对象的特征,封装,继承,多态
封装
隐藏类的信息,不向外界暴露,隐藏实现细节,向外提供特定的方法访问.
成员变量私有化,单例模式是封装的一种体现
继承
在原有类的基础上派生出新的类,新的类具有原有类的所有非私有属性和非私有方法,并扩展新的能力。
优点:可扩展性,代码可重用性
多态
实现多态的三个必要条件:继承,重写,父类的引用指向子类对象
同一事物不同时刻的不同状态
程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定。
多态
同一事物,在不同时候表现不同状态
前提: 继承,方法重写,父类引用指向子类对象
优点:提升程序可维护性,可扩展性 集合 异常 String.valueOf(Object obj) jdbc 接口 mysql servlet Httpetservletrequest
缺点:不能调用子类特有的方法 解决方法:向下转型
多态满足面向对象设计原则中的里氏替换原则.
访问权限
包: 避免类重名
管理类
访问权限控制
public 修饰类,变量,方法 在任何地方都可以访问
private 修饰变量,方法,内部类 只能在本类中访问
proected 修饰变量,方法 在同包中,不同包的子类中访问
默认 修饰类,变量,方法 在同包类中可以访问
继承
优点:
代码复用,易于扩展,维护
缺点:
打破了父类的封装性
父类和子类联系紧密,耦合度高 , 父类一旦发生了变化,子类可能会受影响
结合面向对象设计7大原则来讲. 单一职责原则,组合 聚合复用原则.
应用场景:
多种相同类型有共性属性行为时使用继承
对象创建过程? 从jvm的角度出发,
延伸到对象在内存中的存储空间
new Car(int price);
new Car();
Java类初始化顺序
基类静态代码块,基类静态成员字段(并列优先级,按照代码中出现的先后顺序执行,且只有第一次加载时执行)——>派生类静态代码块,派生类静态成员字段(并列优先级,按照代码中出现的先后顺序执行,且只有第一次加载时执行)——>基类普通代码块,基类普通成员字段(并列优点级,按代码中出现先后顺序执行)——>基类构造函数——>派生类普通代码块,派生类普通成员字段(并列优点级,按代码中出现先后顺序执行)——>派生类构造函数.
静态变量->静态代码块->成员变量->构造代码块->构造方法->普通代码块
java中创建对象的方式
new
反射(Class类的newInstance方法、使用java.lang.relect.Constructor类的newInstance方法) Class.forName(“com.ff.Car”)
-
Class类的newInstance方法
Student student2 = (Student)Class.forName(className).newInstance(); // 或者: Student stu = Student.class.newInstance();
- 1
- 2
- 3
-
Constructor类的newInstance方法
Constructor
constructor = Student.class.getInstance(); Student stu = constructor.newInstance(); - 1
- 2
区别:
两种newInstance方法区别:
- 从包名看,Class类位于java的lang包中,而构造器类是java反射机制的一部分。
- 实现上,Class类的newInstance只触发无参数的构造方法创建对象,而构造器类的newInstance能触发有参数或者任意参数的构造方法。(查看第二部分的源码第2 条解释,)
- Class类的newInstance需要其构造方法是共有的或者对调用方法可见的,而构造器类的newInstance可以在特定环境下调用私有构造方法来创建对象。这点可以从上面源码的第1 条解释可以看出。
- Class类的newInstance抛出类构造函数的异常,而构造器类的newInstance包装了一个InvocationTargetException异常。这是封装了一次的结果。
对象反序列化 IO
对象clone()
对象克隆,浅克隆,深克隆
什么是对象克隆?
创建一个新的对象,将原对象中的数据复制到新对象中
什么时候用?
将请求中接收到数据的对象信息, 拷贝到向dao层传入的参数对象中.
浅克隆?
基本类属于浅克隆,
只将对象中关联的对象的引用地址复制属于浅克隆
深克隆?
只将对象中关联的对象也进行了克隆,多级克隆.
解决多级克隆: 在关联的对象中 继续重写克隆方法
通过对象序列化,反序列化.
构造方法
特征
方法名与类名相同,没有返回值,不可以被void修饰
一个类中可以有多个构造方法, 默认有一个无参的构造(隐式),一旦显示的定义有参的构造方法,默认的就失效,
一般情况,显示的定义无参的构造方法
作用: 初始化创建对象的成员变量
new Car(int age,int a) Car() age=0; String name=null
方法重载,方法重写
在一个类中,有多个名称相同的方法,参数不同.
在继承关系中,父类实现不能满足子类的需求,在子类中重写(覆盖,覆写)父类方法, 功能的扩展,super.父类
为什么构造方法不能重写
构造方法名与当前类名相同, 重写要求与父类方法结构一致.
在子类的构造方法中调用父类的构造方法, 必须放在子类构造方法的第一行.
对象与引用
基本类型
何为引用类型
针对类类型(复合类型), 变量 指向堆中一个对象
值传递和引用传递
基本类型,传值 10
int a = 10;
int b = a; //b=10
引用类型
引用传递, 传递的对象的地址值.
本质都是传值:
Pass By Value 和 Pass By Reference
细化为:值传递(基本类型),引用传递(引用类型 不是对象本身,只是传递对象引用地址)
静态static
表示静态(指在内存只有一份的)
修饰内部类,成员变量,成员方法,代码块
特点:
- 都是随着类的加载而加载
- 优先于对象的存在
- 修饰的成员被所有对象共享
- 可以不创建对象,直接被类调用
**注意:**在static方法内部只能访问类的static属性,不能访问非static属性
抽象类和接口
作用:抽象,功能定义 开闭原则,里氏替换原则都依赖接口,抽象类.
相同点:
都可以包含抽象方法, 不能被实例化.抽象类构造方法和静态方法不可以修饰为abstract
不同点:
抽象类中可以包含成员变量 接口中只能包含静态常量
抽象类中可以包含成员方法 接口中可以包含抽象方法,默认(子类调用),静态(接口调用静态方法jdk8后添加)
抽象类可以包含构造方法,初始化父类成员 接口中不能有构造方法
抽象类中的抽象方法的访问类型可以是public ,protected和默认类型 接口中每一个方法也是隐式抽象的,默认为public abstract 。接口中声明的属性默认为 public static final 的;
从jdk1.8开始,接口中的方法不再是只能有抽象方法(普通方法会被隐式地指定为public abstract方法),他还可以有静态方法和default方法。并且静态方法与default方法可以有方法体!
Object
Java语言不同于C++语言,是一种单根继承结构语言,也就是说,Java中所有的类都有一个共同的祖先。这个祖先就是Object类。
equals() hashCode wait notify finalize() clone()
如图可知,Object类有12个成员方法,按照用途可以分为以下几种
1,构造函数
2,hashCode和equale函数用来判断对象是否相同,
3,wait(),wait(long),wait(long,int),notify(),notifyAll()
4,toString()和getClass
5,clone()函数的用途是用来另存一个当前存在的对象。
6,finalize()用于在垃圾回收
判断两个对象是否相等
基本原理
java中的基本数据类型判断是否相等,直接使用"=="就行了,相等返回true,否则,返回false。
java中的引用类型的对象比较变态,假设有两个引用对象obj1,obj2,obj1==obj2 判断是obj1,obj2这两个引用变量是否相等,即它们所指向的对象是否为同一个对象。言外之意就是要求两个变量所指内存地址相等的时候,才能返回true,每个对象都有自己的一块内存,因此必须指向同一个对象才返回ture。
- 1
- 2
遇到的问题
如果想要自定义两个对象(不是一个对象,即这两个对象分别有自己的一块内存)是否相等的规则,那么必须在对象的类定义中重写equals()方法,如果不重写equals()方法的话,默认的比较方式是比较两个对象是否为同一个对象。
在Java API中,有些类重写了equals()方法,它们的比较规则是:当且仅当该equals方法参数不是 null,两个变量的类型、内容都相同,则比较结果为true。这些类包括:String、Double、Float、Long、Integer、Short、Byte、Boolean、BigDecimal、BigInteger等等,太多太多了,但是常见的就这些了,具体可以查看API中类的equals()方法,就知道了。
- 1
- 2
- 3
重写equals的原则
- 1、先用“==”判断是否相等。
- 2、判断equals()方法的参数是否为null,如果为null,则返回false;因为当前对象不可能为null,如果为null,则不能调用其equals()方法,否则抛java.lang.NullPointerException异常。
- 3、当参数不为null,则如果两个对象的运行时类(通过getClass()获取)不相等,返回false,否则继续判断。
- 4、判断类的成员是否对应相等。
String
特征
一旦创建时赋值后,值不可变.不可以被继承.一旦修改会重新创建应该新的对象
final class String {
final char[] value;
}
创建对象时,先在常量池检查有没有相同的,如果没有就在堆中创建一个字符串对象,值在常量池中,第二次创建时,常量池已经存在,直接让引用变量指向已有的对象地址
为什么不可变
不能改变指的是对象内的成员变量值
private final char[] value;
- 1
String类不可变性的好处? 为什么设计为不可变.
1.便于实现字符串常量池
在Java中,由于会大量的使用String常量,如果每一次声明一个String都创建一个String对象,那将会造成极大的空间资源的浪费。Java提出了String pool的概念,在堆中开辟一块存储空间String pool,当初始化一个String变量时,如果该字符串已经存在了,就不会去创建一个新的字符串变量,而是会返回已经存在了的字符串的引用。
2.使多线程安全
看下面这个场景,一个函数appendStr()在不可变的String参数后面加上一段“bbb”后返回。appendSb()负责在可变的StringBuilder后面加"bbb"。
public class test {
// 不可变的String
public static String appendStr(String s) {
s += “bbb”;
return s;
}
// 可变的StringBuilder
public static StringBuilder appendSb(StringBuilder sb) {
return sb.append(“bbb”);
}
public static void main(String[] args) {
String s = new String(“aaa”);
String ns = test.appendStr(s);//aaabbb
System.out.println(“String aaa>>>” + s.toString());//aaa
// StringBuilder做参数
StringBuilder sb = new StringBuilder(“aaa”);
StringBuilder nsb = test.appendSb(sb);
System.out.println(“StringBuilder aaa >>>” + sb.toString());
}
}
如果程序员不小心像上面例子里,直接在传进来的参数上加上“bbb”.因为Java对象参数传的是引用,所有可变的StringBuffer参数就被改变了。可以看到变量sb在Test.appendSb(sb)操作之后,就变成了“aaabbb”。
有的时候这可能不是程序员的本意。所以String不可变的安全性就体现在这里。
在并发场景下,多个线程同时读一个资源,是安全的,不会引发竞争,但对资源进行写操作时是不安全的,不可变对象不能被写,所以保证了多线程的安全。
3.避免安全问题
在网络连接和数据库连接中字符串常常作为参数,例如,网络连接地址URL,文件路径path,反射机制所需要的String参数。其不可变性可以保证连接的安全性。如果字符串是可变的,黑客就有可能改变字符串指向对象的值,那么会引起很严重的安全问题。
4.加快字符串处理速度
由于String是不可变的,保证了hashcode的唯一性,于是在创建对象时其hashcode就可以放心的缓存了,不需要重新计算。这也就是Map喜欢将String作为Key的原因,处理速度要快过其它的键对象。所以HashMap中的键往往都使用String。
在String类的定义中有如下代码:
private int hash;//用来缓存HashCode
- 1
总体来说,String不可变的原因要包括 设计考虑,效率优化,以及安全性这三大方面。
String的值不可以改变吗?
//通过反射改变String底层数组值 public static void main(String[] args) throws Exception { String s = "Hello World"; System.out.println("s = " + s); //Hello World //获取String类中的value字段 Field valueFieldOfString = String.class.getDeclaredField("value"); //改变value属性的访问权限 valueFieldOfString.setAccessible(true); //获取s对象上的value属性的值 char[] value = (char[]) valueFieldOfString.get(s); //改变value所引用的数组中的第5个字符 value[5] = '_'; System.out.println("s = " + s); //Hello_World System.out.println(s);//Hello_World }
String对象创建方式
Java中创建String的两种方式:
String s1 = "abc"; String s2 = new String("abc");
方法1中,先在常量池中查找有没有"abc"这个字符串对象存在,
如果存在就把常量池中的指向这个字符串对象;
方法2中,不论常量池中中是否已经存在"abc"这个字符串对象,都会新建一个对象。String strA = " abc " ; String strB = " abc " ; String strAA = new String( " abc " ); String strBB = new String( " abc " ); System.out.println(strA == strB);//true System.out.println(strAA == strBB);// false
这句代码的字面量在哪里? String str = new (“hello”);
常量池
创建了几个对象?
1.
String s = new String("abc")创建了几个对象
情况1: 创建两个对象
String s = new String("abc");
直接new对象,此前在字符串常量池中不存在"abc",此种情况在堆中创建一个对象,然后在字符串常量池中创建一个对象
情况2: 创建一个对象
String s = "abc";
String s1 = new String("abc");
因为字符串常量池中已存在"abc",只需要在堆中创建即可.
2.
创建了一个对象,底层优化创建了一个StringBuilder对象,append()追多个字符串对象,只产生一个对象.
String st1 = "a" + "b" + "c";//只创建了一个字符串对象
String st2 = "abc";
System.out.println(st1 == st2);//true
3.
String st1 = "ab";
String st2 = "abc";
String st3 = st1 + "c";//一旦发生拼接中有变量,会重新创建一个字符串对象
System.out.println(st2 == st3);//false
string中常用的方法
构造方法
String()
String("")
String(byte[] b ,0,length,chatset)
String(char[] b ,0,length,chatset)
判断功能
equals, compareTo, contains, isempty startWith
获取功能
length indexOf charAt substring
转换
split valueOf() tocharArray
正则表达式
String,StringBuffer,StringBuilder区别
String 值不可以改变
StringBuffer 值可变 线程安全的
StringBuilder 值可变 线程不安全
StringBuilder和StringBuffer的初始容量都是16,程序猿尽量手动设置初始值。以避免多次扩容所带来的性能问题,默认数组容量扩充为原数组容量的2倍+2。假设这个新容量仍然小于预定的最小值(minimumCapacity),那么就将新容量定为(minimumCapacity),最后推断是否溢出,若溢出,则将容量定为整型的最大值0x7fffffff。
基本类型包装类
因为基本类型不是对象,所以每个基本类提供了一个类包装基本类.使用面向对象方式操作。基本数据类型放在堆栈中,对象放在堆中
最大值,最小值,转换
Integer
int value
自动装箱 Integer a = 10 valueOf() 注意 -128 +127 之间会从缓存池中直接获取
==
自动拆箱 int b = a; intvalue()
Object
Arrays 排序(冒泡,选择,插入 ,快排 , 堆排) ,二分搜索
日期
异常
什么是异常?
运行时异常,可以使用异常处理机制处理 不是错误(OOM)
异常体系
Throwable
Error Exception
RunTimeException
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HB0SnuyY-1634353769845)(D:\