最近面试题

文章目录

简单讲一下是Java跨平台原理

​ 我们所编写的Java程序,是以.java为后缀名的源文件,计算机无法直接执行,需要进行编译环节,编译后为.class为后缀名的字节码文件,将字节码文件交给JVM(java虚拟机)来运行

​ 在不同的操作系统上,安装JVM,由JVM来负责运行.class文件,不同的操作系统中都有与之对应的JVM,只需要写一份java代码,就可以再不同程序上运行,“一处编写,处处运行”,实现就Java程序的跨平台性 ,也称为java具有良好的可移植性

Java语言的特征

简单性 安全性 面向对象 高性能 编译性 解释性 分布式处理 健壮性 开源 跨平台

JDK JRE JVM

  1. JDK Java开发工具包,开发java程序最小的环境为JDK,JDK为Java语言的核心
  2. JRE Java运行时环境,运行java程序最小环境为JRE
  3. JVM Java 虚拟机 ,负责运行和加载.class文件 ,转化给操作系统

标识符

类名,方法名,变量名,字段名给对应元素起的名称叫标识符。

1.标识符可以由字母、数字、下划线(_)、美元符($)组成,但不能包含 @、%、空格等其它特殊字符
2. 不能以数字开头。如:123name 就是不合法
3. 标识符严格区分大小写。如: tmooc 和 tMooc 是两个不同的标识符
4. 标识符的命名最好能反映出其作用,做到见名知意。
5. 标识符不能是Java的关键字
6. null,false ,true也不能用作标识符

关键字

Java中,赋予特定意义的全小写单词叫关键字,有50个,其中有两个保留字:const 和 goto

保留字:是关键字但目前没有明确意义

八大基本类型

byte short int long float double char boolean

类型转换

  1. boolean类型不参与类型转换
  2. 小转大,直接转 – 隐式转换
  3. 大转小,强制转 – 显式转换,格式:byte b = (byte) a;
  4. 浮变整,小数没 – 浮点型转整形是直接舍弃所有的小数部分的,不会四舍五入
    类型能否转换,取决于类型的取值范围,而不是字节数,字节数只能做大概的参考

5条字面值规则

  1. 整数默认为int类型
  2. 小数默认为double类型
  3. byte short char 三种比int小的类型,可以使用范围内的值直接赋值
  4. 字面值后缀:L F D
  5. 字面值前缀:0b-2 0-8 0x-16

5条运算规则

  1. 运算结果的数据类型与最大类型保持一致
  2. 3种比int小的类型,运算时会自动提升成int再运算
  3. 整数运算溢出的问题,一旦溢出,数据就错误了
  4. 浮点数运算不精确
  5. 浮点数的特殊值 Infinity NaN

数组创建方式

静态创建
int[] a ={1,2,3,4,5};
int[] a=new int[]{1,2,3,4,5};
动态创建
int[]a =new int [5];

数组创建过程

  1. 在内存中开辟一块连续的内存空间,用来存放多个相同类型的数据
  2. 给数据完成初始化的过程,给每个元素赋予数组对应类型的默认值,
  3. 数组完成初始化后分配唯一的地址值
  4. 把唯一的地址值交给数组的引用类型变量【数组名】来保存
  5. 要操作数组中的元素,根据变量保存的地址值找到数组,根据下标来操作数组的具体元素,

数组名保存的是数组的地址值,不是数组中每一个具体的元素,数组名是一个引用类型的变量

什么数组?

数组就是内存中的一组连续存储空间,用于存储一组连续数据的有序集合

数组的特性

  1. 数组下标从0开始,最大下标是数组的长度-1
  2. 数组的下标是我们能够进行操作数组的唯一的手段
  3. 访问不属于这个数组的下标,会数组下标越界异常
  4. 数组一旦创建,长度不可改变
  5. 数组的长度我们可以通过数组属性“数组名.length”来获取
  6. 增加或删除数组中的元素,需要创建新长度的数组,无法改变原数组的长度

数组工具类Arrays

  1. toString(数组名):用来查看当前数组的所有具体元素

  2. copyOf(要复制的原数组,新数组的长度):用来进行数组的普通复制/扩容/缩容

    扩容:新数组的长度大于原数组的长度–数组的扩容,不够的位置还是默认值

    缩容:新数组的长度小于原数组的长度–数组的缩容,类似于原数组的截取

    复制:新数组的长度等于原数组的长度–数组的普通复制

  3. (数组名):利用优化后的快速排序算法对数组进行排序,这个排序算法是直接对原数组做修改的

冒泡排序

讲一下面向对象

面向对象OOP也是一种编程思想,这种思想,强调的是结果,所谓的面向对象就是将我们的程序模块化,对象化,我不在意是怎么完成的,我在意的是有对象可以帮我干活

比如:我们想吃饭,不在意是哪个厨师做的,也不在意是哪个骑手送的,只要有厨师做,有骑手派送就好了
行为习惯 思维方式 比如衣服,没有办法给出一个明确的定义,但是,只要我们看到任何一件衣服,我们就自动把它归类到衣服这个分类中,因为你在过去的生活经验中已经见过太多的衣服,积累经验

什么是面向过程

面向过程也是一种编程思想,强调凡是亲力亲为,每一步都是自己做

类有哪些基本特性?各特性的优点?

1.封装

类的封装性为类的成员提供公有、保护和私有等访问权限,目的是隐藏类中的私有变量和类中方法的实现细节。提高程序安全性,低耦合,高内聚

封装属性
  1. 用private修饰属性
  2. 提供这个属性对应的getXxx()获取值与setXxx()设置值
  3. 外界就可以调用公共的get与set方法操作属性了
    注意:访问控制符private修饰的资源只能在本类中使用
封装方法
  1. 用private修饰方法
  2. 我们可以在本类的公共方法里调用这个私有的方法
  3. 外界就可以调用这个公共方法来执行私有方法的功能

2.继承

允许通过继承原有类的某些特性或全部特性而产生全新的类,原有的累称为父类,产生的新类称为子类。子类不仅可以直接继承父类的共性,而且也可以创建它特有的个性,是类之间的一种关系

继承的关键字extends 格式: 子类 extends 父类
继承相当于子类把父类的功能复制了一份,包括私有资源
Java只支持单继承:一个子类只能有一个父类,一个父类可以有多个子类
继承具有传递性:爷爷的功能会传给爸爸,爸爸的功能会传给孙子
子类只可以使用父类的非私有资源,私有资源不可用的原因是不可见
子类可以拥有自己的特有功能
继承是is a 强耦合的关系,依赖性非常强

3.多态

是指在基类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同行为,多态性有两种表现形式:重载和覆盖, 增强软件的灵活性和重用性。

构造方法

格式:没有返回值类型,且方法名与本类类名一致的方法

作用:每次执行构造函数后,都可以创建好一个对应的对象

使用

每个类默认存在一个无参构造
一旦提供了其他的构造函数,默认的无参构造会被覆盖掉
所以如果想不传参数创建对象,就需要手动提供本类的无参构造
构造函数是要创建对象时被触发的,要创建几个对象,构造函数就会被触发几次
具体触发的是哪个构造函数,取决于调用时的参数
构造函数执行完毕,对象就创建成功了
无参构造–没有参数的构造方法
含参构造–包含参数的构造方法,这个参数比较自由,自己决定就好
全参构造–构造方法的参数与本类的所有属性一致,全参构造除了可以创建对象,还可以给对象的所有属性赋值
构造函数是被动触发的,不是我们像普通方法那样主动调用的
全参构造里必须添加给属性赋值的语句,如果不写,即使传入了参数值,属性也不会被赋值

继承中构造方法的使用

我们每次创建对象的时候,都会先触发构造函数
创建子类对象时,会先调用父类的无参构造,因为子类的构造函数中默认存在一个super();
如果父类没有无参构造的话,我们就需要手动指定子类去调用父类的含参构造super(参数);
构造方法不可以被继承,原因是:构造方法名必须是本类的类名,不可能在子类中存在一个父类名字的构造方法

构造器 Constructor 是否可被 override

构造器不能被继承,因此不能被重写,但可以被重载

Constructor不能被继承,所以Constructor也就不能被override。每一个类必须有自己的构造函数,负责构造自己这部分的构造。子类不会覆盖父类的构造函数

类和对象关系

  1. Java语言最基本的单位就是类,相当于类型

  2. 类事一类事物抽共同属性与功能形成的

  3. 可以理解为模板或者设计图纸

    注:类在现实世界中并不存在,只是一种对象的数据类型

对象

每个对象都有三个特点:对象的属性,对象的功能,对象的标识

  1. 对象属性用来描述对象的基本特征
  2. 对象的功能用来描述对象可以完成的操作
  3. 对象的标识是指每个对象在内存中都有唯一地址用于区别其他对象,类似身份证号

类和对象的关系

类是对象的抽象,对象是类的实体,各种操作离不开对象,想干活,先创建对象

创建对象的过程

可以在堆内存中开辟一块空间用来存放对象
这个对象需要进行初始化
初始化完毕以后就会为这个对象生成一个唯一的地址值
在栈内存中开辟一块空间用来存放引用类型Phone类型的变量p
将堆内存中对象的地址值交给引用类型的变量p来保存
后续就可以根据p中保存的地址,找到堆中的对象,并对对象做操作
比如p.brand=“HUAWEI”;就是根据p中保存的地址,找到对象,并对对象的属性赋值

在java中,为什么要有无参数的构造方法,并说出应用场景?

  • 构造方法有两个作用:
    1. 给成员变量赋值
    1. 实例化该类对象
  • 为什么类中要有无参数的构造方法呢?
  • 在框架中, 创建对象默认使用的都是无参数的构造方法,
    所以我们在开发中,一定要显示的定义无参数构造方法

抽象类

被关键字abstract修饰的类是抽象类
一旦一个类中包含了抽象方法,那么这个类必须被声明成一个抽象类
抽象类中的方法不做限制,非常自由:全普 / 全抽 / 半普半抽
抽象类不可以实例化–创建对象
抽象类有构造方法,它自己不用,但是为了子类创建对象时调用
如果一个子类继承了抽象父类,那么有两种解决方案:
1)作为一个抽象子类:不实现/实现部分抽象父类的抽象方法,躺平
2)作为一个普通子类:需要实现抽象父类的全部抽象方法,还债

抽象方法
  1. 被关键字abstract 修饰的方法就是抽象方法
  2. 抽象方法没有方法体{ },直接以分号结束

String,StringBuffer和StringBuilder的区别?

  1. 首先说运行速度,或者说是执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer > String

  2. 再来说线程安全,在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的

​ String:适用于少量的字符串操作的情况,

StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况,

​ StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况行同步控制

​ 常用方法:append() 拼接效率高

String,StringBuffer和StringBuilder能被继承吗?

String 底层 final char[ ] ,不能被继承

StringBuffer和StringBuilder可以被继承

String、StringBuilder、StringBuffer的类的区别:

​ String 是不可变的,任何改变都会产生新的对象,如果改变频繁将产生很多String垃圾碎片,增加JVM的回收负担,占用大量的内存;

​ StringBuilder是可变类,通过可变数组来存储,但不是线程安全的;

​ StringBuffer也是可变类,是线程安全的,使用了Synchronized对操作进行同步控制;

什么是不可变类?

不可变类是指当创建了这个类的实例后,就不允许修改它的值了,也就是说,一个对象一旦被 创建出来,在其整个生命周期中,它的成员变量就不能被修改了。JDK内部自带的很多不可变类包装类型:Interger、Long和String等。

  1. 所有成员变量都被private final
  2. 类添加final修饰符,保证类不被继承
  3. 不提供改变成员变量的方法,包括setter
  4. 通过构造器初始化所有成员,进行深拷贝(deep copy)
  5. 在getter方法中,不要直接返回对象本身,而是克隆对象,并返回对象的拷贝

成员变量和局部变量

区别成员变量局部变量
类中位置不同类里方法外方法里或方法声明上
内存中位置不同堆内存栈内存
声明周期不同随着对象的存在而存在,对象消失而消失随着方法的调用而存在,方法调用完毕而消失
初始化值不同有默认的对应类型的默认值没有默认值,必须手动初始化,来分配内存

13.break与continue

break:直接结束当前循环,跳出循环体

continue:跳出本轮循环,开始一轮循环

return:return是使整个函数返回的,后面的不管是循环里面还是循环外面的都不执行

关键字final

  1. final表示最终
  2. 被final修饰的类是最终类,不可以被继承
  3. 被final修饰的方法是这个方法的最终实现,不可以被重写
  4. 被final修饰的是常量,值不可以被修改,而且常量定义时必须赋值

this与super

  1. this代表的是本类对象的引用,我们可以把this看作是Cat this = new Cat();
    super代表的是父类对象的引用,我们可以把super看作是Father super = new Father();

  2. 当本类的成员变量与局部变量同名时,需要使用this.变量名指定本类的成员变量
    当本类的成员变量与父类的成员变量同名时,需要使用super.变量名指定父类的成员变量

  3. this可以实现调用本类构造方法的功能,不能互相调用,需要写在构造方法首行
    this();表示调用本类的无参构造 this(参数);表示调用本类的对应参数的构造
    super也可以实现调用父类构造方法的功能
    super();表示调用父类的无参构造 super(参数);表示调用父类的对应参数的构造

  4. 注意:super的使用前提是继承,没有父子类关系,就没有super
    注意:this调用构造方法或者super调用构造方法,都必须出现在构造方法的第一行
    注意:如果父类没有无参构造,需要手动在子类构造方法的第一行调用其他的含参构造

重写与重载的比较:

​ 重载:在一个类中的现象,同一个类中,存在方法名相同,参数列表不同的方法(与参数名无关,与参数类型有关)

​ 重载的意义:是为了外界调用方法时,不论传入一个什么样的参数,都可以匹配到对应的同名方法

​ 重写:是指建立了继承关系以后,子类对父类的方法不满意,可以重写,遵循两同两小一大原则

​ 重写的意义:在不修改源码的情况下,进行功能的修改与拓展(OCP原则:面向修改关闭,面向扩展开放)

关键字static

  1. 被static修饰的资源统称为静态资源,可以修饰变量、方法、代码块
  2. 静态资源属于类资源,随着类的加载而加载,优先于对象进行加载
  3. 所以静态资源可以被类名直接调用,不创建对象也可以
  4. 静态资源被全局所有的对象共享
  5. 静态只能调用静态

静态代码块

格式:static{ }
位置:类里方法外
执行时机:随着类的加载而加载,优先于对象进行加载【只加载一次】
作用:用于加载那些需要第一时间就加载

异常

向上抛出
public void play() throws 异常类型1,异常类型2 {}

注意:我们一般不把异常直接抛给main(),因为调用main()的是虚拟机,没人解决了

捕获处理try-catch
 try{
   可能会抛出异常的代码
 }catch(异常的类型 异常的名字){
    万一捕获到了异常,进行处理的解决方案
 }

try-catch-finally

try后面往往会跟着catch或finally,try抛出的任何异常都会被捕获在catch中,在代码终止前的要执行的任务放在finally中

异常的继承结构

异常层次中的根节点Throwable

Error : 目前我们编码解决不了的问题
Exception : 通常可以通过编程解决的问题
1)编译时异常 : 不用运行就已经报错了,比如,少写了分号/父类对象调用子类的特有功能
2)运行时异常 : RunTimeException,编写以及保存的时候没有报错,但是在执行中报错了

注意: 运行时异常是可以通过编译的,不强制要求处理,方法上有默认抛出RunTimeException

写出常见的5个RuntimeException

   (1)java.lang.NullPointerException空指针异常,出现原因:调用了未经初始化的对性爱那个或者不存在的对象。

   (2)ClassNoFoundException 指定类找不到,出现原因:类的名称和路径加载错误,通常是试图通过字符串来加载某个类时可能引发异常。

   (3)NumberFormatException字符串转换为数字异常,出现原因:字符串数据中包含非数字型字符。

   (4)IndexOutOfBoundsException数组下标越界异常

   (5)IllegalArgumentException 方法传递参数错误

   (6)ClassCastException数据类型转换异常

   (7)NoClassDefFoundExcetion 未找到类定义错误

   (8)SQLException SQL异常

   (9)InstantiationException实例化异常

   (10)NoSuchMethodExceptioin 方法不存在异常

接口----

我们通过interface关键字定义接口
接口实现类需要通过关键字implements与接口建立实现关系
接口不可以创建对象/实例化
在Java8中,接口中所有的方法都是抽象方法,方法可以简写,会自动拼接public abstract
接口中没有构造方法,接口实现类调用的构造方法是父类的,不是父接口的
注意:如果一个类没有明确指定父类的话,会默认继承顶级父类Object
接口中没有成员变量,都是静态常量,默认会拼接public static final
接口不是类!!!你可以把接口理解成一个特殊的抽象类,特殊在,所有的方法都是抽象方法
如果一个实现类实现了接口,要不变成一个抽象子类【不实现/实现部分】,要不变成一个普通子类【全部实现】
接口是用来制定规则的【有哪些方法,方法有参数吗?有返回值类型吗?】,并不做具体的实现
接口可以多继承【一个接口可以继承多个接口】 多实现 【一个类可以实现多个接口】

抽象类与接口的比较

抽象类是一个特殊的类,这个类被abstract修饰,并且可以存在抽象方法
接口可以看作是一个特殊的抽象类,接口里面所有的方法都是抽象方法
但是注意:接口不是类,接口使用interface关键字来定义
抽象类中有构造方法,但接口中没有构造方法**(jdk8后可以允许有默认方法和方法体)**
注意:抽象类的子类调用的就是抽象类的构造,但接口的实现类调用的是自己父类的构造方法
抽象类与接口均不可以创建对象
接口可以多继承,抽象类只能单继承
抽象类中可以定义普通的成员变量,但接口中都是静态常量
抽象是后天重构的结果,接口是先天设计的结果

API

hashCode()

作用:返回对象对应的哈希码值,Object中默认实现根据对象的地址值生成哈希码值,对象不同,哈希码值应该不同

toString()

重写前:打印对象的地址值

注意:我们要牢记Object中的默认实现方式,只要与默认实现不同,说明当前类就重写了Object中的实现
至于重写后是什么样的效果,得看具体的重写方式:
自定义类Student重写后: 打印Student类型 + 属性 +属性值【这个是我们自己写的类】
String类重写后:打印的是字符串的具体内容【Java自带的类】

equals()

重写前:等等比较,比较的是两个对象的地址值
注意:我们要牢记Object中的默认实现方式,只要与默认实现不同,说明当前类就重写了Object中的实现
至于重写后是什么样的效果,得看具体的重写方式:
自定义类Student重写后:比较两个对象的类型+属性+属性值
String类重写后:比较的是两个字符串的具体内容

注意1:toString()不是我们主动调用的,是println()层层调用
如果你打印某个类的对象时,不想打印地址值,可以在这个类里添加重写的toString()
注意2:equals()与hashCode()的重写要一致,要重写都重写,要不重写都不重写

== equals 的区别是什么

== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同 一个对象。

​ (基本数据类型== 比较的是值,引用数据类型 == 比较的是内存地址)

equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:

​ 情况1:类没有覆盖equals() 方法。则通过 equals() 比较该类的两个对象时, 等价于通过“==”比较这两个对象。

​ 情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象 的内容相等;若它们的内

容相等,则返回 true (即,认为这两个对象相等)

阐述final、finally、finalize的区别

**final:**修饰符(关键字)有三种用法:如果一个类被声明为final,意味着它不能再派生出新的子类,即不能被继承,因此它和abstract是反义词。将变量声明为final,可以保证它们在使用中不被改变,被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取不可修改。被声明为final的方法也同样只能使用,不能在子类中被重写。
**finally:**通常放在try…catch…的后面构造总是执行代码块,这就意味着.+程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。
**finalize:**Object类中定义的方法,Java中允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize()方法可以整理系统资源或者执行其他清理工作。

单元测试方法

@Test 
public void 方法名 没有参数
 * 测试方法要求:
 *  1.不能有返回值 必须为void
 *  2.不能携带参数
 *  3.测试方法名称  不能叫test
@BeforeEach 当每次执行@Test注解方法时,都会先执行该方法.

正则表达式

通常用来检索,替换那些符合某个模式(规则)的文本,*一种特殊格式的字符串,效验文本信息的

1.匹配不确定次数
     \ 转义字符标记符 如 '\n' 回车
     ^ 匹配输入字行首,正则表达式开头,固定格式
     $ 匹配输入字行尾,正则表达式结尾,固定格式
     * 匹配前面表达式任意次
     + 一次或者多次
     ?零次或一次
  2.匹配确定次数
     {n} 整数,匹配的次数
     {n,} 最少匹配的次数
     {n,m} 最少匹配次数n, 最多匹配次数m
  3.匹配取值区间
     [abc] 匹配所包含任意一个字符
     [^abc] 匹配未包含的任意字符
     [a-z] 字符范围小写  a到z
     [^a-z] 匹配未包含的任意字符 小写a到z
  4.分组匹配
     ( ) 定义为“组” ,如(jpg|png|gif)
     |   将两个匹配条件进行逻辑‘或’ (or) 运算
        
   案例:手机号号码  (开头都是1,第二位从3开始)    ^1[3-9][0-9]{9}$
         匹配邮箱   XXX@XX.XXX   ^[a-zA-Z0-9-_]+@[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+$

序列化和反序列化

序列化 指将对象的状态转换为可以存储传输形式的过程,在序列化期间,对象将其当前当前状态写入到临时或持久存储区,以后可以通过从存储区中读取或者反序列化对象的状态,重新创建对象

序列化:利用ObjectOutputStream,把对象的信息,按照固定的格式转成一串字节值输出并持久保存到磁盘
反序列化:利用ObjectInputStream,读取磁盘中之前序列化好的数据,重新恢复成对象

特点

  1. 需要序列化的文件必须实现Serializable接口,用来启用序列化功能
  2. Serializable接口是一个空接口,里面一个方法都没有,作用是用来当做标志,标志这个类可以序列化/反序列化
  3. 不需要序列化的数据可以修饰成static,原因:static资源属于类资源,不随着对象被序列化输出
  4. 在反序列化时,如果和序列化的版本号不一致,无法完成反序列化
  5. 常用与服务器之间的数据传输,序列化成文件,反序列化读取数据
  6. 常用使用套接字流在主机之间传递对象

为什么反序列化版本号需要与序列化版本号一致?

在反序列化时,JVM会拿着反序列化流中的serialVersionUID与序列化时相应的实体类中的serialVersionUID来比较,如果不一致,就无法正常反序列化,出现序列化版本不一致的异常InvalidClassException。

而且我们在定义需要序列化的实体类时,如果没有手动添加UID,
Java序列化机制会根据编译的class自动生成一个,那么只有同一次编译生成的class才是一样的UID。

如果我们手动添加了UID,只要这个值不修改,就可以不论编译次数,进行序列化和反序列化操作

内部类

创建内部类对象必须先创建外部类对象,在创建内部类对象

1) 内部类可以直接访问外部类中的成员,包括私有成员
2) 外部类要访问内部类的成员,必须要建立内部类的对象
3) 在成员位置的内部类是成员内部类
4) 在局部位置的内部类是局部内部类
成员内部类被Private修饰以后,无法被外界直接创建创建对象使用
所以可以创建外部类对象,通过外部类对象间接访问内部类的资源

静态资源访问时不需要创建对象,可以通过类名直接访问

访问静态类中的静态资源可以通过”. . . ”链式加载的方式访问

线程

创建线程方式一:

​ 继承thread类,重写run( ) 方法实现 ,run( )方法体里是具体业务,创建线程对象,new 关键字对应线程新建状态,通过线程对象调用.start( ) 将线程加入到就绪队列,.start( )对应线程的就绪状态

public class TestThread1 {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        /*.run()如果直接这样调用,是没有多线程抢占执行的效果的
        * 只是把这两句话看作普通方法的调用,谁先写,就先执行谁*/
        //t1.run();
        //t2.run();
        /*.start()对应的状态就是就绪状态,会把刚刚新建好的线程加入到就绪队列之中,至于什么时候执行,就是多线程执行的效果,需要等待OS选中分配CPU
        * 执行的时候start()底层会自动调用我们重写的run()种的业务
        * 线程的执行具有随机性,也就是说t1-t4具体怎么执行, 取决于CPU的调度时间片的分配,我们是决定不了的*/
        t1.start();//以多线程的方式启动线程1,将当前线程变为就绪状态
        t2.start();//以多线程的方式启动线程2,将当前线程变为就绪状态
    }
}
//1.自定义一个多线程类,然后让这个类继承Thread
class MyThread extends Thread{
    /*多线程编程实现的方案1:通过继承Thread类并重写run()来完成的 */
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            /*getName()表示可以获取当前正在执行的线程名称,由于本类继承了Thread类,所以可以直接使用这个方法*/
            System.out.println(i+"="+getName());
        }
    }
}
创建多线程方式二:

实现runnable接口,重写run( )方法,run( )方法体里是具体业务,接口无法创建对象,需要Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码

public class TestThread2 {
    public static void main(String[] args) {
        MyRunnable target = new MyRunnable();
        //启动线程自己没有,需要与Thread建立关系
        Thread t1 = new Thread(target);
        Thread t2 = new Thread(target);
        t1.start();
        t2.start();
    }
}
//1.自定义多线程类
class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            /*自定义类与父接口Runnable中都没有获取名字的方法
            * 所以还需要从Thread中找:
            * currentThread():静态方法,获取当前正在执行的线程对象
            * getName():获取当前线程的名称*/
            System.out.println(i+"="+Thread.currentThread().getName());
        }
    }

Thread 和Runnable 的区别

一个类继承Thread,则不适合资源共享(单继承)。但是如果实现了Runable接口的话,则很容易的实现资源共享

实现Runnable接口比继承Thread类所具有的优势:

1):适合多个相同的程序代码的线程去处理同一个资源

2):可以避免java中的单继承的限制

3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

线程的五种状态:

创建 > 就绪 > 运行/阻塞 > 终止

线程状态与代码对照

  1. **新建状态(new):**新创建了一个线程对象

  2. **就绪状态(runnable)😗*其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权

  3. **运行状态(running)😗*当CPU调度了处于就绪状态的线程时,此线程才是真正的执行,即进入到运行状态

  4. **阻塞状态(blocked)😗*处于运状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入就绪状态才有机会被CPU选中再次执行.
    根据阻塞状态产生的原因不同,阻塞状态分成三种:
    等待阻塞:运行状态中的线程执行wait()方法,本线程进入到等待阻塞状态

    同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),它会进入同步阻塞状态其他阻塞:调用线程的sleep()或者join()或发出了I/O请求时,线程会进入到阻塞状态.当sleep()状态超

    时.join()等待线程终止或者超时或者I/O处理完毕时线程重新转入就绪状态

  5. **终止状态(dead)😗*线程执行完了或者因异常退出了run()方法,该线程结束生命周期

单例设计模式

底层实现思路:

  • 对本类构造方法私有化,防止外部调用构造方法创建对象
  • 创建全局唯一的对象,也做私有化处理
  • 通过自定义的公共方法将创建好的对象返回(类似封装属性后的getXxx() )

方案一:饿汉式

public class Singleton1 {
    public static void main(String[] args) {
        MySingle single1 = MySingle.getSingle();
        MySingle single2 = MySingle.getSingle();
        System.out.println(single1 == single2);//true
    }
}
class MySingle{
    //1.提供构造方法,并将构造方法私有化,
    //为了防止外界随意创建本类对象
    private MySingle(){ }

    //2.创建本类对象,并将对象也私有化
    //由于静态资源只能调用静态资源,所以single对象也需要设置成静态
    private static MySingle single = new MySingle();

    //3.提供公共的访问方式,返回创建好的对象
    //为了不通过对象,直接调用本方法,需要将本方法设置为静态
    public static MySingle getSingle(){
        return single;
    }
}

方案二:懒汉式(延迟加载的思想)

/*	1.饿汉式:不管你用不用这个类的对象,都会直接先创建一个
	2.懒汉式:先不给创建这个类的对象,等你需要的时候再创建--延迟加载的思想
延迟加载的思想:是指不会在第一时间就把对象创建好占用内存
              而是什么时候用到,什么时候再去创建对象
	3.线程安全问题:由于我们存在唯一的对象single2,并且多条语句都操作了这个变量
  如果将程序放到多线程的环境下,就容易出现数据安全的问题,所以解决方案:
   1) 将3条语句都使用同步代码块包裹,保证同步排队的效果
   2) 由于getSingle2()只有这3条语句,所以也可以将本方法设置为同步方法*/
public class Singleton2 {
    public static void main(String[] args) {
        MySingle2 single1 = MySingle2.getSingle2();
        MySingle2 single2 = MySingle2.getSingle2();
        System.out.println(single1 == single2);
    }
}
class MySingle2{
    //6.2创建一个静态的唯一的锁对象
    static Object o = new Object();
    //1.私有化本类的构造方法
    private MySingle2(){ }
    //2.创建的是本类对象的引用类型变量,用来保存对象的地址值,默认值是null
    private static MySingle2 single2 ;
    //3.提供公共的get方法
    synchronized public static MySingle2 getSingle2(){
        //4.判断之前是否创建过对象,之前创建过就直接走return
        //没有创建过,才走if,创建对象并将对象返回
        //6.有共享数据+多条语句操作数据,所以尽量提前处理,避免多线程数据安全隐患
        //6.1 解决方案1:加同步代码块
        //6.2 解决方案2:将本方法getSingle2()设置为同步方法
        synchronized (o) {//静态方法中使用的锁对象也得是静态的
            if (single2 == null) {//single2还是默认值,说明之前没有创建过对象
                single2 = new MySingle2();//没创建过才创建,并赋值给single2
            }
            return single2;
        }
    }
}

集合

img

Collection接口

List 接口 [ 有序,数据有下标,可重复]

ArrayList

​ 底层是数组,当数据量较大时,查询快,增删慢

LinkedList

​ 底层是链表,当数据量较大时,查询慢,增删快(中间数据慢,首位操作快)

Set 接口 [ 无序,数据无下表,不可重复]

HashSet

​ 双列集合,底层数据是entry[ ], (key values)

TreeSet

Map

HashMap

HashMap 的存储的过程:(HashMap结构是数组+链表 或者 数组+红黑树)

  1. HashMap底层的Entry[ ]数组,初始容量为16,加载因子是0.75f,扩容按约为2倍扩容
  2. 当存放数据时,会根据hash(key)%n算法来计算数据的存放位置,n就是数组的长度,其实也就是集合的容量
  3. 当计算到的位置之前没有存过数据的时候,会直接存放数据
  4. 当计算的位置,有数据时,会发生hash冲突/hash碰撞
    解决的办法就是采用链表的结构,在数组中指定位置处以后元素之后插入新的元素
    也就是说数组中的元素都是最早加入的节点
  5. 如果链表的长度>8且数组长度>64时,链表会转为红黑树,当链表的长度<6时,红黑树会重新恢复成链表

TreeMap

Collection Collections 有什么区别?

java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操

作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为

各种具体的集合提供了 大化的统一操作方式,其直接继承接口有List与Set。

Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进

行排序、搜索以及线程安全等各种操作。

自动拆箱和装箱

包装类型Integer的创建方式:

1)Integer i1 = new Integer(5); 没有高效的效果,new一次,创建一个包装类对象
2)Integer i2 = Integer.valueOf(5); 有高效的效果,数据在-128~127的范围内,才有高效的效果
3)Integer i3 = 5; 自动装箱:编译器会把int类型5装箱,变成Integer,底层调用的方法:valueOf(5)

自动装箱与自动拆箱:

1)Integer i3 = 5; 自动装箱【基本类型 到 包装类型】:
编译器会把int类型的5装箱,变成Integer类型,底层调用的方法:valueOf(5)
2)int n = i3;自动拆箱【包装类型 到 基本类型】:
编译器会把包装类型的i3中的数据取出来,把这个值赋值给基本类型int
底层调用的方法:i3.intVlaue();

为什么要使用包装类型?

对基本类型进行了包装,提供了丰富的功能,包装类型是基本类型的拓展

  • 基本数据类型方便、简单、高效,但泛型不支持、集合元素不支持
  • 不符合面向对象思维
  • 包装类提供很多方法,方便使用,如 Integer 类 toHexString(int i)、parseInt(String s) 方法等等

类加载器

类加载器加载.class文件的流程为 加载>验证>准备>解析>初始化

类加载条件

1.当创建一个类的实例时,比如使用 new 关键字或者反射、克隆、反序列化

2.当调用类的静态方法时,即使用字节码 invodestatic 指令

3.当使用类或接口的静态字段时(final 常量除外),比如使用 getstatic 或者 putstatic 指令

4.当使用 java.lang.reflect 包中的方法反射类的方法时

5.当初始化子类时,要求先初始化父类

6.作为启动虚拟机,含有 main() 方法的那个类

jvm 会创建三种类加载器

启动类加载器
Bootstrap ClassLoader 主要负责加载系统的核心类,如 rt.jar 中的 java 类,我们在 Linux 系统或 Windows 系统使用 java,都会安装 jdk,lib 目录里其实里面就有这些核心类

扩展类加载器
Extension ClassLoader 主要用于加载 lib\ext 中的 java 类,这些类会支持系统的运行

应用类加载器
Application ClassLoader 主要加载用户类,即加载用户类路径(ClassPath)上指定的类库,一般都是我们自己写的代码5

类加载器双亲委派机制

​ java中为了保证类加载的安全,使用了双亲委派机制。优先从“启动类加载器”中加载,这个称为“父”,“父”无法加载到,再从“扩展类加载器”中加载,这个称为“母”,这就是双亲委派机制。如果都加载不到,才会考虑从“应用类加载器”中加载,知道加载到为止。

在没有执行main()的情况下,成功编译了一个Java类,这一说法正确吗?

正确,虽然Java编译器规定程序的入口点为staticmain,但是没有main()还是能够编译,只是不能运行

类中mian方法可以被声明为private?

Java中main方法必须是public ,如果是private,编译时不会报错,运行时会出错

main 方法是启动的时候由 JVM 进行加载的,public 的可访问权限是最高的,所以需要声明为 public;

main 方法可以重载吗?

可以 ,但是只有固定格式public static void main(String[] args)可被JVM识别

main方法可以被继承吗?

可以,main方法作为应用程序的入口比较特殊外,其他情况下与正常的静态方法是没什么区别的

main方法能被其他方法调用么?

可以,,调用后记得关闭,否则会进入死循环

如何在Java中完成垃圾回收

Java中,对象不被引用时,垃圾回收就会发生,对象会被自动销毁,调用System.gc()方法或Runtime.gc()方法

在多线程中,如何确保资源不会同时被多个线程使用?

在多线程中,可以通过使用同步的概念来控制对多个线程之间共享的资源的访问。使用synchronized关键字,我们可以确保只有一个线程可以一段时间内使用共享资源

举一个在Java类中使用指针的例子

Java中没有指针

所有异常类的基类是什么?

Java.Lang.throwable

在以下4种特殊情况下,finally块不会被执行:
  1. 在finally语句块中发生了异常
  2. 在前面的代码中用了System.exit()退出程序
  3. 程序所在的线程死亡
  4. 关闭CPU
java中序列化如果有些字段不想进行序列化怎么办

@transient: transient关键字的作用就是:阻止用这个关键字修饰的不想被序列化的变量序列化;transient只能修饰变量,不能修饰类和方法

@JsonIgnore注解作用:jackson api 在json序列化时将pojo中的一些属性忽略掉,标记在属性或者方法上,返回的json数据即不包含该属性。

try-catch-finally 中,如果 catch return 了, finally 还会执行吗?

会执行,在 return 前执行

静态代码块和静态方法那个先执行?

静态代码块在项目启动时就执行,自动执行

静态方法被调用时执行

数据库中什么是事务?

是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作

数据库创建表时没有添加主键,之后想要添加主键时

alter table 需要添加主键表名 add primary key 列名

SQL 更改 字段属性 如 int 改为 varchar ?

alter table 表名 modify column 字段名 类型

SQL中触发器(trgger on)

CREATE TRIGGER js_insert_trigger ON 教师 FOR INSERT AS BEGIN PRINT('禁止插入记录!') ROLLBACK TRANSACTION END GO

数据库delete和truncate的区别?

truncate
删除表中的内容,不删除表结构,释放空间;

delete
删除内容,不删除表结构,但不释放空间

内存空间
truncate删除数据后重新写数据会从1开始,而delete删除数据后只会从删除前的最后一行续写;内存空间上,truncate省空间

为什么equals 方法时连同重写 hashcode 方法?

1.一致性,即:当两个对象 equals 比较为 true,那么 hashcode 值应当相等,反之亦然,因为当两个对象hashcode 值相等,但是 equals 比较为 false,那么在 HashMap 中会产生链表,影响查询性能。

2.成对重写,即重写 equals 就应当重写 h-8ashcode

Map迭代的方式有几种?
  1. Set keySet() 根据键找value值方式遍历集合
  2. Set<Map.Entry<K,V>> entrySet() 获取键值对对象通过迭代器或增强for遍历取值
  3. Collection values() 通过Map集合中values方法拿到所有的值
  4. V get(Object key) 根据key找获取对应的值
创建的4种线程池的使用?
  1. newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  2. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  3. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
  4. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
怎样将数组去重?
  1. 先将数组转化成集合,然后使用distinct 方法去重
  2. 利用hashSet 集合 不可重复性去重
  3. 利用list.contains 方法去重
Springboot配置加载顺序是什么?

1.properties 2. yml 3. 系统环境变量 4.命令行参数

springboo启动方式?
  1. 运行带有main方法类

  2. 通过命令行 java -jar 的方式

  3. 通过spring-boot-plugin的方式

怎样通过反射创建对象?

Class.forName(“类的全路径”);

类名.class

对象.getClass();

clazz.newInstance();//执行无参构造创建对象

时连同重写 hashcode 方法?

1.一致性,即:当两个对象 equals 比较为 true,那么 hashcode 值应当相等,反之亦然,因为当两个对象hashcode 值相等,但是 equals 比较为 false,那么在 HashMap 中会产生链表,影响查询性能。

2.成对重写,即重写 equals 就应当重写 h-8ashcode

Map迭代的方式有几种?
  1. Set keySet() 根据键找value值方式遍历集合
  2. Set<Map.Entry<K,V>> entrySet() 获取键值对对象通过迭代器或增强for遍历取值
  3. Collection values() 通过Map集合中values方法拿到所有的值
  4. V get(Object key) 根据key找获取对应的值
创建的4种线程池的使用?
  1. newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  2. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  3. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
  4. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
怎样将数组去重?
  1. 先将数组转化成集合,然后使用distinct 方法去重
  2. 利用hashSet 集合 不可重复性去重
  3. 利用list.contains 方法去重
Springboot配置加载顺序是什么?

1.properties 2. yml 3. 系统环境变量 4.命令行参数

springboo启动方式?
  1. 运行带有main方法类

  2. 通过命令行 java -jar 的方式

  3. 通过spring-boot-plugin的方式

怎样通过反射创建对象?

Class.forName(“类的全路径”);

类名.class

对象.getClass();

clazz.newInstance();//执行无参构造创建对象

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值