目前没空进行排版,已排版链接:https://blog.csdn.net/qq_36010886/article/details/126612902
JDK 和 JRE
JDK 是Java工具包,包含JRE(Java运行环境)+编译器。
JRE是Java运行时环境,包含Java虚拟机(JVM)、Java类库、Java命令和其他组件。
Java理解的代码叫字节码文件(.class),通过编辑器转换成机器可以理解的二进制文件。
Java程序从源代码到运行大致是以下过程:
.java ==> javac编译 ==>.class ==>解释器&JIT ==>二进制 ==>CPU执行
HotSpot 采用了惰性评估(Lazy Evaluation)的做法,根据二八定律,消耗大部分系统资源的只有那一小部分的代码(热点代码),而这也就是 JIT 所需要编译的部分。JVM 会根据代码每次被执行的情况收集信息并相应地做出一些优化,因此执行的次数越多,它的速度就越快。JDK 9 引入了一种新的编译模式 AOT(Ahead of Time Compilation),它是直接将字节码编译成机器码,这样就避免了 JIT 预热等各方面的开销。JDK 支持分层编译和 AOT 协作使用。
为什么不全部使用AOT?
与Java 语言的动态特性有关。比如CGLIB 动态代理使用的是 ASM 技术,而这种技术大致原理是运行时直接在内存中生成并加载修改后的字节码文件也就是 .class 文件,如果全部使用 AOT 提前编译,也就不能使用 ASM 技术了。为了支持类似的动态特性,所以选择使用 JIT 即时编译器。
注释有哪几种形式
单行
多行
文档
标识符和关键字的区别是什么
标识符是一个名字
关键字是被赋予特殊含义的标识符。例如:private、public
成员变量与局部变量的区别
语法形式 :从语法形式上看,成员变量是属于类的,而局部变量是在代码块或方法中定义的变量或是方法的参数;成员变量可以被 public,private,static 等修饰符所修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。
存储方式 :从变量在内存中的存储方式来看,如果成员变量是使用 static 修饰的,那么这个成员变量是属于类的,如果没有使用 static 修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。
生存时间 :从变量在内存中的生存时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动生成,随着方法的调用结束而消亡。
默认值 :从变量是否有默认值来看,成员变量如果没有被赋初始值,则会自动以类型的默认值而赋值(一种情况例外:被 final 修饰的成员变量也必须显式地赋值),而局部变量则不会自动赋值。
静态变量有什么作用?
静态变量可以被类的所有实例共享。无论一个类创建了多少个对象,它们都共享同一份静态变量。
通常情况下,静态变量会被 final 关键字修饰成为常量
Java 中的八种基本数据类型
基本类型 位数 字节 默认值 取值范围
byte 8 1 0 -128 ~ 127
short 16 2 0 -32768 ~ 32767
int 32 4 0 -2147483648 ~ 2147483647
long 64 8 0L -9223372036854775808 ~ 9223372036854775807
char 16 2 ‘u0000’ 0 ~ 65535
float 32 4 0f 1.4E-45 ~ 3.4028235E38
double 64 8 0d 4.9E-324 ~ 1.7976931348623157E308
boolean 1 false true、false
这八种基本类型都有对应的包装类分别为:Byte、Short、Integer、Long、Float、Double、Character、Boolean 。
包装类型的缓存机制
Java 基本数据类型的包装类型的大部分都用到了缓存机制来提升性能。
Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 True or False。
面向对象和面向过程的区别
两者的主要区别在于解决问题的方式不同:
面向过程把解决问题的过程拆成一个个方法,通过一个个方法的执行解决问题。
面向对象会先抽象出对象,然后用对象执行方法的方式解决问题。
另外,面向对象开发的程序一般更易维护、易复用、易扩展。
对象的相等和引用相等的区别
对象的相等一般比较的是内存中存放的内容是否相等。
引用相等一般比较的是他们指向的内存地址是否相等
构造方法有哪些特点?是否可被 override?
构造方法特点如下:
名字与类名相同。
没有返回值,但不能用 void 声明构造函数。
生成类的对象时自动执行,无需调用。
构造方法不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。
面向对象三大特征
封装:
利用抽象的数据类型将数据以及对数据的操作封装成一个独立的实体,通过对象的对外提供的接口来进行访问。优点:减少耦合,提高了可重用性等
继承:
继承实现了 IS-A 关系,继承遵循里氏替换原则,子类对象必须能够替换掉所有父类对象。 子类可当做父类来使用,也可以说子类是父类的引用。父类引用指向子类对象称为向上转型 。
多态:
多态分为编译时多态和运行时多态。
编译时多态主要指编译时多态方法的重载,Java在编译时能够确定执行重载方法中的哪一个;运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定。例如方法覆盖表现出两种多态性,当对象引用本类实例时,为编译时多态,否则为运行时多态。
运行时多态有三个条件:继承、覆盖(重写)、向上转型
接口和抽象类有什么共同点和区别?
共同点 :
都不能被实例化。
都可以包含抽象方法。
都可以有默认实现的方法(Java 8 可以用 default 关键字在接口中定义默认方法)。
区别 :
接口主要用于对类的行为进行约束,你实现了某个接口就具有了对应的行为。抽象类主要用于代码复用,强调的是所属关系。
一个类只能继承一个类,但是可以实现多个接口。
接口的字段只能是 static 和 final 类型的,而抽象类的字段没有这种限制。接口的成员只能是 public 的,而抽象类的成员可以有多种访问权限。
从设计层面上看,抽象类提供了一种 IS-A 关系,那么就必须满足里式替换原则,即子类对象必须能够替换掉所有父类对象。而接口更像是一种 LIKE-A 关系,它只是提供一种方法实现契约,并不要求接口和实现接口的类具有 IS-A 关系。 从使用上来看,一个类可以实现多个接口,但是不能继承多个抽象类。
抽象
抽象类和抽象方法都使用 abstract 关键字进行声明。
抽象类一般会包含抽象方法,抽象方法一定位于抽象类中。抽象类和普通类最大的区别是,抽象类不能被实例化,需要继承抽象类才能实例化其子类
1
2
接口(接口是抽象类的延伸)
在 Java 8 之前,它可以看成是一个完全抽象的类,也就是说它不能有任何的方法实现。从 Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类。
接口的成员(字段 + 方法)默认都是 public 的,并且不允许定义为 private 或者 protected。接口的字段默认都是 static 和 final 的。
1
2
3
深拷贝和浅拷贝区别了解吗?什么是引用拷贝?
浅拷贝:拷贝对象和原始对象的引用类型引用同一个对象。
深拷贝 :深拷贝会完全复制整个对象,拷贝对象和原始对象的引用类型引用不同对象。
另外使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象
== 和 equals() 的区别
== 对于基本类型和引用类型的作用效果是不同的:
对于基本数据类型来说,== 比较的是值。
对于引用数据类型来说,== 比较的是对象的内存地址。
因为 Java 只有值传递,所以,对于 == 来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。
equals() 不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。equals()方法存在于Object类中,而Object类是所有类的直接或间接父类,因此所有的类都有equals()方法。
equals() 方法存在两种使用情况:
类没有重写 equals()方法 :通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象,使用的默认是 Object类equals()方法。
类重写了 equals()方法 :一般我们都重写 equals()方法来比较两个对象中的属性是否相等;若它们的属性相等,则返回 true(即,认为这两个对象相等)。
hashCode() 有什么用
hashCode() 的作用是获取哈希码(int 整数),也称为散列码。这个哈希码的作用是确定该对象在哈希表中的索引位置。
hashCode()定义在 JDK 的 Object 类中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。另外需要注意的是: Object 的 hashCode() 方法是本地方法,也就是用 C 语言或 C++ 实现的,该方法通常用来将对象的内存地址转换为整数之后返回。
散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象HashMap)
为什么重写 equals() 时必须重写 hashCode() 方法?
因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。
如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,hashCode 值却不相等。
思考 :重写 equals() 时没有重写 hashCode() 方法的话,使用 HashMap 可能会出现什么问题。
总结 :
equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。
两个对象有相同的 hashCode 值,他们也不一定是相等的(哈希碰撞)。
String、StringBuffer、StringBuilder 的区别
相同:
都是char数组
不同:
String 被final修饰,是不可变的对象,每次对String类型进行改变,实际上等同于生成一个新的String
StringBuffer 是线程安全的。append方法有被synchronized进行修饰
StringBuilder 是线程不安全的。并没有做任何安全控制。
String s1 = new String(“abc”);这句话创建了几个字符串对象?
会创建 1 或 2 个字符串对象。堆中字符串对象、字符串常量池。
String.intern() 是一个 native(本地)方法,其作用是将指定的字符串对象的引用保存在字符串常量池中
重写与重载
重写(Override)。存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的一个方法。
为了满足里式替换原则,重写有有以下两个限制:
1、子类方法的访问权限必须大于等于父类方法;
2、子类方法的返回类型必须是父类方法返回类型或为其子类型。
使用 @Override 注解,可以让编译器帮忙检查是否满足上面的两个限制条件。
重载(Overload)。存在于同一个类中,指一个方法与已经存在的方法名称上相同,但是参数类型、个数、顺序至少有一个不同。
访问权限
private、protected 以及 public,如果不加访问修饰符,表示包级可见。
default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
public : 对所有类可见。使用对象:类、接口、变量、方法
protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
1
2
3
4
5
6
7
8
9
switch
switch 从 Java 7 开始,可以在 switch 条件判断语句中使用 String 对象。switch 不支持 long,是因为 switch 的设计初衷是对那些只有少数的几个值进行等值判断
static
静态变量: 又称为类变量,类所有的实例都共享静态变量。
实例变量: 每创建一个实例就会产生一个实例变量,它与该实例同生共死
静态方法:在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法(abstract)。只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字
静态语句块在类初始化时运行一次。
静态内部类不能访问外部类的非静态的变量和方法。
静态导包:在使用静态变量和方法时不用再指明 ClassName,从而简化代码,但可读性大大降低
初始化顺序:静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。
1
2
3
4
5
6
7
8
9
10
11
12
反射
反射可以用于判断任意对象所属的类,获得 Class 对象,构造任意一个对象以及调用一个对象。最重要的用途就是开发各种通用框架。很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 Bean),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要加载的对象。
反射会额外消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射。另外,反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题
1
反射的实现原理
反射类及反射方法的获取,都是通过从列表中搜寻查找匹配的方法,所以查找性能会随类的大小方法多少而变化;每个类都会有一个与之对应的Class实例,从而每个类都可以获取method反射方法,并作用到其他实例身上;
反射也是考虑了线程安全的,放心使用。反射使用软引用relectionData缓存class信息,避免每次重新从jvm获取带来的开销;反射调用多次则生成新代理Accessor, 而通过字节码生存的则考虑了卸载功能,所以会使用独立的类加载器;当找到需要的方法,都会copy一份出来,而不是使用原来的实例,从而保证数据隔离;
调度反射方法,最终是由jvm执行invoke0()执行;
1
2
3
4
5
泛型
泛型的本质是为了参数化类型。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
泛型的上下限。为了解决泛型中隐含的转换问题,Java泛型加入了类型参数的上下边界机制
1
泛型的类型擦除原则是:
1、消除类型参数声明,即删除<>及其包围的部分。
2、根据类型参数的上下界推断并替换所有的类型参数为原生态类型:
1>如果类型参数是无限制通配符或没有上下界限定则替换为Object,
2>如果存在上下界限定则根据子类替换原则取类型参数的最左边限定类型(即父类)。
1
2
3
4
为了保证类型安全,必要时插入强制类型转换代码。自动产生“桥接方法”以保证擦除类型后的代码仍然具有泛型的“多态性”。泛型原始类型.就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,无论何时定义一个泛型,相应的原始类型都会被自动提供,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。
Java编译器是通过先检查代码中泛型的类型,再进行类型擦除,然后进行编译。类型擦除会造成多态的冲突,而JVM解决方法就是桥接方法.基本类型不能作为泛型类型,因为当类型擦除后,ArrayList的原始类型变为Object,但是Object类型不能存储int值,只能引用Integer的值。而使用list.add(1)是因为Java基础类型的自动装箱拆箱操作
桥接方法是 JDK 1.5 引入泛型后,为了使Java的泛型方法生成的字节码和 1.5 版本前的字节码相兼容,由编译器自动生成的方法。我们可以通过Method.isBridge()方法来判断一个方法是否是桥接方法,在字节码中桥接方法会被标记为ACC_BRIDGE和ACC_SYNTHETIC,其中ACC_BRIDGE用于说明这个方法是由编译生成的桥接方法,ACC_SYNTHETIC说明这个方法是由编译器生成,并且不会在源代码中出现。
1
注解
作用:
1、生成文档,通过代码里标识的元数据生成javadoc文档。
2、编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。
3、编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。
4、运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例。
5、注解是不支持继承的
1
2
3
4
5
6
7
8
9
Throwable
Throwable 是 Java 语言中所有错误与异常的超类。Throwable 包含两个子类:Error(错误)和 Exception(异常).throwable 包含了其线程创建时线程执行堆栈的快照,它提供了 printStackTrace() 等接口用于获取堆栈跟踪数据等信息
Error(错误 Error 类及其子类)
此类错误一般表示代码运行时 JVM 出现问题。通常有 Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。比如 OutOfMemoryError:内存不足错误;StackOverflowError:栈溢出错误。此类错误发生时,JVM 将终止线程。这些错误是不受检异常,非代码性错误。
1
Exception(异常)
程序本身可以捕获并且可以处理的异常。Exception 这种异常又分为两类:运行时异常和编译时异常。
1
异常关键字
try 用于监听
catch 用于捕获异常
finally finally语句块总是会被执行
throw 用于抛出异常
throws 用在方法签名中,用于声明该方法可能抛出的异常
1
2
3
4
5
6
7
8
9
异常捕获处理的方法通常有
1、try-catch 捕获多个异常类型,并对不同类型的异常做出不同的处理
2、try-catch-finally 常规语法
3、try-finally try块中引起异常,异常代码之后的语句不再执行,直接执行finally语句。try块没有引发异常,则执行完try块就执行finally语句
4、try-with-resource 更加优雅的写法,避免IO关闭异常
final, finally, finalize的区别
final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。
finally是异常处理语句结构的一部分,表示总是执行。
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。
1
2
3
finally遇见如下情况不会执行:
1、在前面的代码中用了System.exit()退出程序
2、finally语句块中发生了异常
3、程序所在的线程死亡
4、关闭CPU
1
2
3
4
SPI机制
SPI 即 Service Provider Interface ,专门提供给服务提供者或者扩展框架功能的开发者去使用的一个接口。SPI 将服务接口和具体的服务实现分离开来,将服务调用方和服务实现者解耦,能够提升程序的扩展性、可维护性。很多框架都使用了 Java 的 SPI 机制,比如:Spring 框架、数据库加载驱动、日志接口、以及 Dubbo 的扩展实现等等。
通过 SPI 机制能够大大地提高接口设计的灵活性,但是 SPI 机制也存在一些缺点,比如:
需要遍历加载所有的实现类,不能做到按需加载,这样效率还是相对较低的。
当多个 ServiceLoader 同时 load 时,会有并发问题。
什么是序列化?什么是反序列化?
序列化: 将数据结构或对象转换成二进制字节流的过程
反序列化:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程
**序列化的主要目的是通过网络传输对象或者说是将对象存储到文件系统、数据库、内存中。**对于不想进行序列化的变量,使用 transient 关键字修饰。
transient 还有几点注意:
transient 只能修饰变量,不能修饰类和方法。
transient 修饰的变量,在反序列化后变量值将会被置成类型的默认值。例如,如果是修饰 int 类型,那么反序列后结果就是 0。
static 变量因为不属于任何对象(Object),所以无论有没有 transient 关键字修饰,均不会被序列化。
序列化(serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓冲,或经由网络中发送),以留待后续在相同或另一台计算机环境中,能恢复原先状态的过程。依照序列化格式重新获取字节的结果时,可以利用它来产生与原始对象相同语义的副本。对于许多对象,像是使用大量引用的复杂对象,这种序列化重建的过程并不容易。面向对象中的对象序列化,并不概括之前原始对象所关系的函数。这种过程也称为对象编组(marshalling)。从一系列字节提取数据结构的反向操作,是反序列化(也称为解编组、deserialization、unmarshalling)。
1
Java IO 流
O 即 Input/Output,输入和输出。数据输入到计算机内存的过程即输入,反之输出到外部存储(比如数据库,文件,远程主机)的过程即输出。数据传输过程类似于水流,因此称为 IO 流。IO 流在 Java 中分为输入流和输出流,而根据数据的处理方式又分为字节流和字符流。
Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。
InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
那为什么 I/O 流操作要分为字节流操作和字符流操作呢?
1)字符流是由 Java 虚拟机将字节转换得到的,这个过程还算是比较耗时
2)字节流不能确定编码类型,使用字节流的过程中很容易出现乱码问题
IO设计模式
1)装饰器模式。可以在不改变原有对象的情况下拓展其功能。
2)适配器模式。主要用于接口互不兼容的类的协调工作。适配器模式中存在被适配的对象或者类称为 适配者(Adaptee) ,作用于适配者的对象或者类称为适配器(Adapter) 。适配器分为对象适配器和类适配器。类适配器使用继承关系来实现,对象适配器使用组合关系来实现。
3)工厂模式。工厂模式用于创建对象,NIO 中大量用到了工厂模式。
4)观察者模式。NIO 中的文件目录监听服务使用到了观察者模式。NIO 中的文件目录监听服务基于 WatchService 接口和 Watchable 接口。WatchService 属于观察者,Watchable 属于被观察者。
BIO、NIO 和 AIO 的区别
BIO 属于同步阻塞 IO 模型 。
NIO I/O 多路复用模型/同步非阻塞 IO 模型。是支持面向缓冲的,基于通道的 I/O 操作方法。 对于高负载、高并发的(网络)应用,应使用 NIO。
AIO 也就是 NIO 2.异步 IO 模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作.
语法糖
语法糖(Syntactic sugar) 代指的是编程语言为了方便程序员开发程序而设计的一种特殊语法,这种语法对编程语言的功能并没有影响。例如:Java 中的 for-each(增强 for 循环) 就是一个常用的语法糖,其原理其实就是基于普通的 for 循环和迭代器。
JVM 其实并不能识别语法糖,Java 语法糖要想被正确执行,需要先通过编译器进行解糖,也就是在程序编译阶段将其转换成 JVM 认识的基本语法,Java 中真正支持语法糖的是 Java 编译器。
Java 中最常用的语法糖主要有泛型、自动拆装箱、变长参数、枚举、内部类、增强 for 循环、try-with-resources 语法、lambda 表达式等。
开发原则SOILD
S 单一职责SRP(实现高内聚、低耦合的指导方针)
O 开放封闭原则OCP(对扩展开放,对修改关闭) 设计模式中模板方法模式和观察者模式都是开闭原则的极好体现
L 里氏替换原则LSP(任何基类可以出现的地方,子类也可以出现)
I 接口隔离法则(客户端不应该依赖那些它不需要的接口)
D 依赖倒置原则DIP(要依赖抽象,而不要依赖具体的实现)
合成/聚合复用原则(要尽量使用对象组合,而不是继承关系达到软件复用的目的)
迪米特法则(系统中的类,尽量不要与其他类互相作用,减少类之间的耦合度)
CAP 理论
C 代表一致性 (Consistency)
A 代表可用性 (Availability)
P 代表分区容错性 (Partition tolerance)
CAP理论 告诉我们C、A、P三者不能同时满足,最多只能满足其中两个
ACID 事务理论
Atomicity(原子性):事务是一个不可分割的整体,事务内所有操作要么全做成功,要么全失败。
Consistency(一致性):务执行前后,数据从一个状态到另一个状态必须是一致的
Isolation(隔离性): 多个并发事务之间相互隔离,不能互相干扰。
Durablity(持久性):事务完成后,对数据库的更改是永久保存的,不能回滚。
A原子性由undo log日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql;C一致性一般由代码层面来保证; I隔离性由MVCC来保证;D持久性由内存+redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,事务提交的时候通过redo log刷盘,宕机的时候可以从redo log恢复。
设计模式总结
单例模式 一般用于配置文件、枚举类等全局配置场景
策略模式 一般用于特定事件需要封装算法、结构,
适配器模式 一般用于代码中功能数据处理,serviceImpl
静态代理模式 用于远程代理、虚拟代理
动态代理模式 常用于日志记录、安全控制等(AOP面向切面编程)。
外观模式 常用于导航搜索
责任链模式 常用于任务区分,各司其职。
工厂模式 抽象管理,便于调用者使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
疑问点
一、private修饰的方法可以通过反射访问,那么private的意义是什么?
Java的private修饰符不是为了绝对安全设计的,而是对用户常规使用Java的一种约束。但是你还是能够通过反射看到清晰的类结构。
优点:
1:能够运行时动态获取类的实例,大大提高了系统的灵活性和扩展性;
2:与java动态编译相结合,可以实现无比强大的功能。
缺点:
1:使用反射的性能较低;
2:使用反射来说相对不安全;
3:破坏了类的封装性,可以通过反射来获取这个类的属性,和私有方法
1
2
3
4
5
6
7
二、字符串有长度限制吗?是多少?
首先字符串的内容是由一个字符数组 char[] 来存储的,由于数组的长度及索引是整数.数组的最大长度可以使【0~2^31-1】通过计算是大概4GB。在class文件中u2表示的是无符号数占2个字节单位,2个字节就是16位 ,那么2个字节能表示的范围就是2^16- 1 = 65535 。
其实是65535,但是由于JVM需要1个字节表示结束指令,所以这个范围就为65534了。 超出这个范围在编译时期是会报错的,但是运行时拼接或者赋值的话范围是在整形的最大范围。
1
三、 HttpServletRequest 能当bean注入么
可以使用@Autowired HttpServletRequest request,在Spring的bean中轻松的注入HttpServletRequest。原因是Spring在容器初始化的时候就将HttpServletRequest注册到了容器中。源码在WebApplicationContextUtils.registerWebApplicationScopes(ConfigurableListableBeanFactory, ServletContext)中实现了这个功能。这个方法是在AbstractApplicationContext.refresh()中进行调用的,也就是在Spring容器初始化的时候,将web相关的对象注册到了容器中。
四、解释内存中的栈(stack)、堆(heap)和静态区(static area)的用法
堆区(通过new关键字和构造器创建的对象放在堆空间):
1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身.
3.一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 。
1
2
3
栈区(对象实例在堆中分配好以后,保存一个4字节的内存地址,用来定位该对象实例的位置):
1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
4.由编译器自动分配释放,存放函数的参数值,局部变量的值等.
1
2
3
4
5
6
7
方法区(静态区);
1.跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
3.全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。
1
2
3
4
5
五、sleep() 和 wait() 有什么区别
sleep方法属于Thread类中的静态方法,调用该方法导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。sleep可以在任何地方使用(使用范围)
wait()属于Object的成员方法,对此对象调用wait方法导致本线程放弃对象锁,使得其他线程可以使用同步控制块或者方法。wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用。唤醒需要手工调用notify()或者notifyAll()方法。
六、Java 中会存在内存泄漏么
理论上Java因为有垃圾回收机制(GC)不会存在内存泄露问题(这也是Java被广泛使用于服务器端编程的一个重要原因);然而在实际开发中,可能会存在无用但可达的对象,这些对象不能被GC回收,因此也会导致内存泄露的发生。例如:hibernate的Session(一级缓存)中的对象属于持久态,垃圾回收器是不会回收这些对象的,然而这些对象中可能存在无用的垃圾对象,如果不及时关闭(close)或清空(flush)一级缓存就可能导致内存泄露。可查看内存回收机制。
————————————————
版权声明:本文为CSDN博主「静看º一季花开花落」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_36010886/article/details/126612902