首页 > 编程语言 >java和集合和JVM

java和集合和JVM

时间:2025-01-06 14:57:47浏览次数:1  
标签:java 对象 引用 线程 内存 JVM 集合 方法 加载

Java 语言有哪些特点?
面向对象(封装,继承,多态),平台无关性( Java 虚拟机实现平台无关性),可靠性(具备异常处理和自动内存管理机制)。

.java文件(java源代码文件)经javac编译器编译成.class文件(字节码文件) JVM 类加载器再加载字节码文件,解释器逐行解释执行;
OpenJDK 开源;

Java 和 C++ 的区别?
Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。Java 有自动内存管理垃圾回收机制(GC),不需要程序员手动释放无 用内存。指针直接访问内存,内存不安全,Java 不用。

continue、break 和 return 的区别是什么?
continue:指跳出当前这一次循环,继续下一次循环。break:指跳出整个循环体;return结束这个方法

Java 中有 8 种基本数据类型和占用字节以及对应包装类,分别为:
6 种数字类型: 4 种整数型:byte、 short、 int、 long
2 种浮点型: float、double
1 种字符类型: char
1 种布尔型: boolean。
除char的包装类是Character,int的包装类是Integer,其他都是首字母大小

浮点数默认double类型,加F才是单精度类型

包装类和基本类型区别
基本类型不能用于泛型,而包装类型可。
基本数据:类型局部变量虚拟机栈中的局部变量表中,成员变量(未被 static 修饰 )在堆中。几乎所有对象实例都存在于堆中。

包装类型的缓存机制?大部分的包装类用了缓存来提升性能。
Byte,Short,Integer,Long 这 4 种整数类型的包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,
Character 创建了数值在 [0,127] 范围的缓存数据,
Boolean 直接返回 True or False。超出对应范围才去创建新的对象。

long
long 类型数据要在后面加上 L,否则将作为整型解析。超过long整型大小的数据用BigInteger( 内部使用 int[] 数组)表示;二进制补码中,最高位0 表示正数,1 表示负数,其余位表示数值。所以,最大的正数是除最高位外的所有位都 为 1。如果再加 1,就会溢出,变成一个负数;

自动装箱与拆箱了解吗?原理是什么?
装箱:将基本类型用它们对应的引用类型包装起来;隐式调包装类的静态方法valueOf()。
拆箱:将包装类型转换为基本数据类型; 隐式调包装类对象的实例方法XXvalueOf();

浮点数运算精度丢失:
无限循环的小数和一些数(0.2)转二进制是无限循环的,只能被截断。创建BigDecimal对象,把浮点数传进去解决 ;

成员变量与局部变量的区别?
1、成员变量属于类,局部变量在代码块或方法中定义的变量或是方法的参数;
2、成员变量可以被 访问控制符(public,private)和static 等修饰符所修饰,而局部变量不能被访问控制符及 static 所修饰;但是,成员变量和局部变量都能被 final所修饰。
3、内存中存储位置:如果成员变量是使用 static 修饰的,那么这个成员变量是属于类的,如果没有使用 static 修饰,这个成员变量是属于·实例对象的。而对象存在于堆内存,局部变量则存在于栈内存。成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动生成,随着方法的调用结束而消亡。
4、默认值:成员变量默认赋值(被 final 修饰的成员变量也必须显式地赋值),而局部变量不会自动赋值;

为什么成员变量有默认值?
编译器(javac)会报没默认值错。而成员变量可能是运行时(反射等方法手动赋值,而局部变量不行)赋值,误报会影响用户体验,所以采用自动赋默认值。;

static,(被 static 关键字修饰,只在类加载时分配到内存的元空间所以只分配一次,为了省内存)
1、静态变量(被 static 关键字修饰)。被类的所有实例共享,无论一个类创建多少个对象,静态变量只会被分配一次内存,省内存。通过类名来访问的,如果被 private关键字修饰无法这样访问)。通常,静态变量会搭配 final 关键字修饰成为常量。;
2、静态方法属于类,在类加载时就会分配内存,而非静态成员属于实例对象,在对象实例化之后才存在。静态方法先存在,当时内存中还不存在非静态成员(实例变 量和实例方法)。

字符型常量和字符串常量的区别?
字符常量是单引号引起的一个字符,字符串常量是双引号引起的 0 个或若干个字符。含义 : 字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代 表一个内存地址值。char 在 Java 中占两个字节;

重载和重写
1、重载就是同一个方法根据输入数据的不同,做出不同的处理,重写就是当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖1 父类 方法,重载发生在同一个类中,访问控制符和返回值类型可以相同可以不同、方法名必须相同,方法参数表必须不同(参数类型、个数、顺序有一个不 同)。
2、重写发生在父类和子类之间,访问控制符范围>=父类,返回值类型和抛出的异常范围要<=父类。
子类不能重写父类访问修饰符为 private/final/static 的方法,但是能够再次声明被 static 修饰的方法。

可变长参数(编译成 class后转换成一个数组),所谓可变长参数就是允许在调用方法时传入不定长度的参数。就比如下面的这个 printVariable 方法就可以接受 0 个或者多个参数。另外,可变参数只能作为函数的最后一个参数;

面向过程一个个方法的执行解决问题。oop面向对象先抽象出对象,然后用对象执行方法解决问题(易维护、易复用、易扩展)。
面向对象三大特征:封装是指把属性封装在对象内部,只提供可以被外界访问的方法来操作属性。继承是使用已有类作为基础建立新类,新类可增加新的属性或功能,通过继承,可以快速创建新的类,可以提高代码的重用(子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问)。多态指父类的引用可指向任意子类的实例。因为子类可重写方法,所以引用类型变量调用的方法对同一输入有不同的响应
调用的方法到底是哪个类的,程序运行期间才能确定;多态不能调用“只在子类存在但在父类不存在”的方法;子类重写了父类的方法,真正执行的是子类的方法,如 果没有,执行的是父类的。

内部类的概念与优点
内部类就是写在类或者方法中的类,只能被这个外部类所用。
内部类的优点:防止类重名,实现多继承。
内部类只能间接通过访问外部类的方法来实例化内部类。
内部类可以隐藏你不想让别人知道的操作,实现代码的封装性。
内部类可以直接使用外部类的私有和保护属性,方便实用。
内部类可以减少开发类的数量。

This和Super关键字
This是当前对象,哪个对象调用的方法,方法内this就是谁。用来调本类的属性,方法,构造器,区分局部变量和全局变量,this只能在构造器中调构造器且必须放在第一个语 句。
Super代表直接父类的引用,用来调父类的属性、方法、构造器,调构造器用法同this。用于子父类的属性和方法同名时直接调父类的属性方法。

访问控制符范围
访问控制符用于控制类、字段、方法和构造函数的可见性和访问范围
public同一个工程(代码复制、包复制到jre)、protected同一个包和子类(不管在不在一个包)、缺省(默认)同一个包、private同一个类

引用相等和对象的相等的区别
String str = new String("hello"); str是引用指向实例对象的地址(引用存放在栈内存中),new 创建实例对象(对象实例在堆内存中),
引用相等一般比他们指向的内存地址,对象相等一般比内存中存放的内容是否相等(new新开辟一块内存,重写后的equals比引用,是比的他俩指向的内存地址里的内 容);

构造方法有哪些特点?是否可被 override?
一个类没有构造方法,默认有个无参构造器。
构造方法名与类名相同
没有返回类型,且不能是 void 。
自动执行:在生成类的对象时,构造方法会自动执行,无需显式调用。

接口和抽象类(有抽象方法)共同点和区别?
共同点:1、都不能被实例化。2、都可以包含抽象方法。3、都可以有默认的实现方法(Java 8 可以用 default 关键字在接口中定义默认有方法体的方法)。
区别:1、类之间是单继承的,但类可以实现多个接口。2、接口中的成员变量只能是 public static final 类型的,必须有初始值且不能修改,而抽象类的成员变量默认 default,可在子类中被重新定义,也可被重新赋值。抽象类的抽象方法不能是private,接口的抽象方法只能是public,
接口用于让类必须有接口定义的抽象方法。而抽象类主要用于让子类都继承相同的实现的方法,提高代码复用。

深拷贝和浅拷贝区别了解吗?什么是引用拷贝?
例子,Person person1 = new Person(new Address("武汉"));
引用拷贝,顾名思意,只复制引用,也就是引用所指向对象的地址(两个引用指向同一个对象)
浅拷贝:基本类型只拷贝值,在堆创建一个新的对象(区别于引用拷贝的一点,引用拷贝只复制原对象的地址),复制原对象内部的引用类型属性的值,拷贝对象和原对 象共用同一个内部对象。(两个引用指向两个对象,两个对象的内部的引用还指向同一个内部对象)
深拷贝:完全复制整个对象,包括其内部对象。

native方法,也就是用 C 语言或 C++ 实现的

Object 类方法
toString(), hashCode(),equals(Object obj) clone()
notify()随机唤醒一个在此对象锁上等待的线程(All所有), wait()放锁并等待nms,finalize()实例被垃圾回收器回收的时,释放对象的内存

== 和 equals() 的区别
,基本数据类型比值。引用类型比地址(指向对象的内存地址)
Object 类 equals()内就是
,但String类重写了equals(),比的是指向对象里的内容

创建 String 类型的对象时,先用常量池中已存在的值和要创建的值相同的对象,把它赋给当前引用。没有就在常量池中重新创建一个 String 对象。

hashCode() 有什么用?
获取哈希码(int 整数),也称为散列码。确定该对象在哈希表中的索引位置。
为什么要有 hashCode?
比equals()执行效率更高,先执行它,如果code相等再用equals()判断是否相等
为什么重写 equals() 时必须重写 hashCode() 方法?
因为两个对象相等, 他俩的hashCode 值必须相等。如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象, hashCode 值却不相等。

1.final, finally, finalize的区别
final用于声明属性方法和类
final类无法被继承。且所有方法隐式加final。比如java.lang.String类就是final类。
final方法:该方法无法被子类重写,但是可以被继承。
final变量:该属性一定要有初始值,要么在定义时初始化,要么在构造器中初始化。如果该变量是基本类型,那基本类型数据不能修改。如果是引用类型,它指向的对象引用不可变,但是对象里的内容可变。

finally是异常处理语句结构的一部分,表示总是执行。如果try里有一个return语句,会在return前执行finally中的代码。
finalize是Object类的方法,gc回收该对象时会调用此方法,可以重写此方法提供垃圾收集时其他资源的回收,比如关闭文件等。

new在堆创建,“”引起的字符串常量对象在常量池

String 为什么是不可变的?
1、内部用字符数组存字符串,而字符数组用了final修饰且为私有的,并且String 类没有修改这个字符串的方法。
2、并且String类也加了final,避免了子类破坏 String 不可变
StringBuilder 与 StringBuffer的字符数组没final 和 private 关键字修饰。

String的字符数组不可变,+操作,在字符串常量池创建新的字符串常量对象,引用更新(指向它地址)
StringBuilder字符数组可变,直接改字符数组,引用不更新。

字符串拼接用“+” 还是 StringBuilder?
用StringBuilder
用“+”拼接字符串对象,转成字节码,实际上是创建StringBuilder 对象,通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() ,在堆上创 建一个新的对象,返回 。但StringBuilder 对象不复用,每次+都创建并销毁浪费资源。

String、StringBuffer、StringBuilder 的区别?
操作少量的字符串: 用 String
单线程操作大量字符串: 适用 StringBuilder
多线程操作大量字符串: 适用 StringBuffer (StringBuffer 和StringBuilder方法几乎一样,但StringBuffer都加了synconized)

字符串常量池的作用了解吗?
在堆中创建字符串对象”ab“后,把字符串对象”ab“的引用(地址)放到字符串常量池,(提高字符串复用性,减少内存消耗)
例子
// 在堆中创建字符串对象”ab“
// 将字符串对象”ab“的引用保存在字符串常量池中
String aa = "ab";
// 把字符串常量池中字符串对象”ab“的引用赋给 bb
String bb = "ab";
System.out.println(aa==bb);// true

String s1 = new String("abc");这句话创建了几个字符串对象?
常量池有字符串对象“abc”会创建 1 个,没有创建 2 个字符串对象,一个字符串变量对象,一个字符串变量对象。
String s1 = new String("abc");如果常量池中不存在字符串对象“abc”,那么它会在堆上创建字符串变量对象和"abc"字符串变量对象,把"abc"的引用赋给字符串对 变量对象内部的数组。

String#intern 方法有什么作用?
是一个 native(本地)方法,返回的是字符串对象内部字符串常量对象(如“xxx”)的引用, 或"xxx”字符串对象的引用

String 类型的变量和常量做“+”运算时发生了什么?
String 类型的常量+时会优化。String str3 = "str" + "ing"; 编译器会给你优化成 String str3 = "string"; 。

序列化:数据结构或对象转换成二进制字节流。反序列化时,会检查 serialVersionUID 是否和当前类的 serialVersionUID 一致。 强烈推荐每个序列化类都手动指定其 serialVersionUID,如果不手动指定,那么编译器会动态生成默认的 serialVersionUID。不想序列化的变量,用 transient 关键字修饰

java.lang 包中的 Throwable 类
Throwable 类
Exception类 :程序自己可以处理的异常,可以通过 catch 来进行捕获。
Checked Exception (受检查异常 ,Java 代码在编译过程中,如果受检查异常没有被 catch或者throws 关键字处 理的话,就没办法通过编译)
UncheckedException(不受检查/运行时异常,可以不处理)。
Error类:程序无法处理的错误 ,不建议通过catch捕获 。例如 Java 虚拟机运行错误(Virtual MachineError)、虚拟机内存溢出(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等 。这些异常发生时,(JVM)会终止线程;

Throwable 类有两子类,错误Error 和异常 Exception 。Error程序无法处理会立即停止运行(比如oom),Exception是程序可以处理的异常,可以用try-catch处理,或者throws抛出。Exception又分为两类:检查型异常也叫编译时异常和非检查型也叫运行时异常,检查型异常必须用try-catch块处理,或者throws抛出,否则编译不通过,比如IOException(文件找不到异常的父类)。不受检查/运行时异常可以不处理,比如空指针、数组越界。

常见异常

Checked Exception :文件找不到。UncheckedException:空指针、数组越界

throw和throws的区别
throw:用于在方法体内手动抛出一个异常实例对象,可以用try…catch处理也可以上抛给调用者
throws:用于在方法签名中用,不处理异常只上抛给调用者

异常使用注意?
抛出更具体的异常比如字符串转换为数字格式错误的时候应该抛出NumberFormatException而不是其父类IllegalArgumentException。

Throwable 类常用方法有哪些?
String getMessage(): 返回异常发生时的简要描述
String toString(): 返回异常发生时的详细信息
String getLocalizedMessage(): 返回异常对象的本地化信息。使用 Throwable 的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返 回的信息与 getMessage()返回的结果相同
void printStackTrace(): 在控制台上打印 Throwable 对象封装的异常信息

try-catch-finally 如何使用?
try块:用于捕获异常。其后可接n个 catch 块,如果没有 catch 块,则必须跟一个 finally 块。
catch块:用于处理 try 捕获到的异常。
finally 块:无论是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中有 return 语句时, finally 语句块将在方法返回之前被执行。( 先把返回值存起来,执行完finally 语句,再返回)

finally 中的代码一定会执行吗?
虚拟机被终止运行, 执行这段程序的线程死亡。关闭 CPU(关机)。

什么是泛型
用泛型能写出类型安全、复用性高的代码。泛型会在编译时就检查添加数据类型是否和刚开始指定的类型一样,不会等到运行再抛类型转换异常(ClassCastException)。

泛型的优点:1、可以指定类型,添加时类型不一样编译就能进行检查,避免了运行时的 ClassCastException
2、从泛型集合中取元素时,编译器自动转换(不用手动类型转换)集合中元素的类型(把Object转换你指定的)。

泛型的使用方式有哪几种?
泛型接口(实现泛型接口,可指定可不指定类型)、泛型类(实现泛型类,必需指定类型)、泛型方法。
静态泛型方法,由于静态方法的加载先于类的实例化,在类的实例化时真正传类型前,静态的方法的加载就已经完成了,所以静态泛型方法不能使用类上声 明的泛型。只能使用自己声明的

泛型擦除

Java 编译阶段,泛型参数会被擦除为直接父类,编译后的字节码中只保留原始类型。(为了兼容 Java 5 之前的代码,不然jvm识别不了)

List<String> stringList = new ArrayList<>();
编译时,List<String> 会被擦除为 List,只保留原始类型

项目中哪里用到了泛型?
自定义接口通用返回结果 CommonResult ,通过参数 T 可返回具体类型数据。
自定义泛型方法,封装缓存击穿的通用解决方法

什么是反射?
是程序在运行时动态获取类所有信息,比如获取并调用任意一个类的所有属性和方法。Class 类对象封装一个类的方法、变量等信息

获取Class 对象的四种方式

知道具体类,类.class,对象不会进行初始化。Class.forName()传入类的全路径获取。通过对象.getClass()获取。通过类加载器 xxxClassLoader.loadClass()传入类路径获取

框架Jdbc,Spring IOC使用的动态代理就是基于反射实现的,反射机制实质上就是不需要实例化对象(不需要new出来),将你的类进行初始化,对于私有属性也能通过反射机制进行赋值,靠的就是当前类的.class文件。
注解也是基于反射实现。

反射机制的实现要借助于4个类
class,Constructor,Field,Method;其中class代表类对 象,Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象。通过这四个对象我们可以粗略的看到一个类的各个组成部分。

如何禁用java的反射机制
将默认公共无参构造器私有化,因为反射机制靠公共无参构造器进行初始化,所以运行时候会报错。

反射的优缺点?
让代码更加灵活、为各种框架提供开箱即用的功能。

反射的应用场景?
1、框架中大量使用的动态代理、注解,依赖反射实现。
2、注解依赖反射实现,通过反射获取类/属性/方法/方法入参上的注解后,执行一些逻辑。

Spring 框架用反射(Reflection)来实现依赖注入(DI)、动态代理、配置处理

  1. 依赖注入(Dependency Injection)
    Spring 使用反射来创建和注入对象。例如,当 Spring 容器初始化时,它会通过反射来创建 bean 实例,并通过反射来创建它依赖的Bean通过构造函数注入到这些实例中。

  2. AOP(面向切面编程)
    Spring使用动态代理(Dynamic Proxy)来实现横切点(如事务管理、日志记录等)。Spring 通过反射生成代理对象,在这些代理对象中应用切面(Advice)。

示例:假设你有一个服务类 MyService 需要被代理:
public class MyServiceImpl implements MyService {
@Override
public void doSomething() {
// Implementation
}
}
Spring 使用反射创建A的代理对象,通过 AOP 切面将额外的功能(如日志记录)织入到方法调用中。

  1. 配置处理
    Spring 还使用反射来处理配置,例如从配置类或 XML 配置文件中读取 bean 的定义,并根据这些定义创建 bean 实例。

示例:在 XML 配置中定义一个 bean:

Spring 通过反射读取 MyBean 类的构造函数和属性,并创建实例。
  1. 动态加载类
    Spring 可以通过反射动态加载类,特别是在模块化或插件化的应用程序中。例如,Spring Boot 的自动配置机制会根据 classpath 中的类动态选择合适的配置。

示例:使用 Class.forName() 动态加载类:

Class<?> clazz = Class.forName("com.example.MyClass");
Object instance = clazz.getDeclaredConstructor().newInstance();
5. 使用反射获取和设置字段
Spring 有时使用反射来访问和修改类的字段,特别是在处理依赖注入时。比如,Spring 的 AutowiredAnnotationBeanPostProcessor 通过反射处理 @Autowired 注解:

Field field = target.getClass().getDeclaredField("someField");
field.setAccessible(true);
field.set(target, injectedValue);
6. Spring 数据绑定
Spring 的数据绑定(例如将请求参数绑定到 Java Bean)也使用反射来设置属性值:

BeanWrapper beanWrapper = new BeanWrapperImpl(targetObject);
beanWrapper.setPropertyValue("propertyName", value);
总结
在 Spring 中,反射主要用于以下几个方面:

依赖注入:通过反射创建 bean 实例并注入依赖项。
动态代理:创建代理对象以实现 AOP 功能。
配置处理:解析和处理配置文件或类。
动态加载类:在运行时动态加载和实例化类。
数据绑定:将外部数据(如表单参数)绑定到 Java 对象的属性。
反射在 Spring 框架中扮演着重要角色,使得框架能够实现高度的灵活性和扩展性。然而,反射也有性能开销,因此在设计应用时需要权衡其使用。

注解
是继承了Annotation接口的接口
用于修饰类、方法、变量,程序在编译或者运行时根据注解信息执行一些逻辑

注解用法
继承Annotation 接口的接口,
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {

}

public interface Override extends Annotation{

}

注解的解析方法有哪几种?
编译时扫描并处理,比如某个方法使用@Override 注解,编译器在编译的时候就会检测当前的方法是否重写了父类对应的方法。
运行时通过反射处理:像框架中自带的注解(比如 Spring 框架的 @Value、@Component)都是通过反射来进行处理的。

何谓 SPI?
SPI 即 Service Provider Interface ,字面意思是:“给服务提供者(程序员)使用的接口”。如数据库加载驱动
API。接口和实现都是放在实现方的包中。调用方通过接口直接调用已经实现好的功能,不需要关心具体的实现细节。

什么是序列化和反序列化?
序列化:将数据结构或对象转换成二进制字节流的过程(使用场景:将 Java 对象保存在二进制文件中,在网络传输 Java 对象)
反序列化:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程 (使用场景:从二进制文件中读取Java 对象,网络传输Java 对象的接收方)

有些变量不想序列化怎么办?
使用 transient 关键字修饰(序列化和反序列化时看作不存在)。在反序列化后变量值是类型的默认值。(int 类型,那么反序列后结果就是 0)。
static 变量因为不属于任何对象,所以不会被序列化。

为什么不推荐使用 JDK 自带的序列化?
性能差,存在安全问题

什么是语法糖

是编程语言为了方便程序员开发程序而设计的一种特殊语法,这种语法对编程语言的功能并没有影响。实现相同的功能,基于语法糖写出来的代码往往更简单简洁且更易阅读。Java 语法糖要想被正确执行,需要先通过编译器进行解糖,也就是在程序编译阶段将其转换成 JVM 认识的基本语法,比如 for-each 就是一个常用的语法糖,其原理其实就是基于普通的 for 循环和迭代器。

io
缓冲流将数据加载至缓冲区(字节数组),一次性读取/写入多个字节(可手动new 字节数组,让非缓冲流也实现),从而避免频繁的 IO 。缓冲区的大小默认为 8192 字节,也可以BufferedInputStream(InputStream in, int size) 构造方法来指定。
BufferedReader (字符缓冲输入流)和 BufferedWriter(字符缓冲输出流)内部也有一个字节数组作为缓冲区。不过,是用来操作字符信息。
打印流
System.out 获取一个 PrintStream(字节打印流) 对象,print方法实际调用的是 PrintStream 对象的 write 方法。PrintStream 属于字节打印流,。PrintStream 是 OutputStream 的子类

RandomAccessFile
文件内容是文件中保存的数据,元数据是属性比如大小信息、创建和修改时间。RandomAccessFile 中有一个文件指针表示下一个要被写入或者读取的字节所处位置。我们可其seek(long pos) 方法设置文件指针的偏移量(距文件开头 pos 个字节处)。读写权限模式有:r : 只读模式。rw: 读写模式rwd : 相对于 rw,rwd 同步更新对“文件的内容”的修改到外部存储设备。s包含元数据。
实现大文件的 断点续传和多线程传文件分片 。何谓断点续传?简单来说就是上传文件中途暂停或失败(比如遇到网络问题)之后,不需要重新上传。

Writer/reder结尾都是字符流
流像是 java进程内存和磁盘的文件或网络间 有方向的水流
fileWriter只能操作文件,所以加了处理流能包装一个后缀类的子类(想操作文件、数组、管道就传对应类,点进去看属性)
关处理流会自动关节点流,

文件对象要创建文件或多级目录
输出节点流(写) 创建流对象(传入文件对象)时选择是否追加,创建流对象就清空。流一定要关闭才能写入。用write方法(字节把字节数组往文件写,字符)
输入节点流(读) 。用read方法(字节往字节数组读,字符往字符数组读。每次读数组长度个字节数,返会真正读入的字节数,读到头返回-1)

//FileReader只能按操作系统默认的编码将字节解码为字符流,把硬盘里文本文件对应的一堆字节解码成字符,与文本文件的存时用的编码不一样会乱码。而用FileInputStream先把文本文件读取为字节流再用InputStreamReader把字节流按指定的解码格式转为字符流。
File file = new File(INDEX_FILE_PATH+"index_"+ MONTH_TXT +".txt");//用路径创建一个文件对象
InputStreamReader read = new InputStreamReader(new FileInputStream(file), "UTF-8");//用文件对象创建一个文件字节输出流(因为文件是用UTF-8编码格式,把字符编成字节存到硬盘的),用文件字节输出流创建一个字符输入流,用UTF-8把字节解码成字符(因为文件是用UTF-8编码格式,把字符编成字节存到硬盘的)

//用字符输入流创建字符缓冲流(有行读函数),因为字符缓冲流内有一个8192的字符缓冲区,调行读函数时,缓冲区空则先读8192个字符到缓冲区
//行读函数读到换行符返回,下次调行读函数从字符缓冲区读,减少IO
BufferedReader bufferedReader = new BufferedReader(read);//
String lineTxt = null;
//
while ((lineTxt = bufferedReader.readLine()) != null) {
    // 处理每一行数据
}
输出缓冲流(写)  。用writeline方法
输入缓冲流(读)  。用readline方法
拷贝文件(读一行 写入一行)

IO 流了解吗?
按流的方向:输入流,输出流 。
按流的数据单位:字节流,字符流。
流的功能:节点流:直接从数据源或目标读写数据,如FileInputStream、FileOutputStream。
包装流(也叫处理流):包装已存在的流,增强读写
缓冲流:,如BufferedInputStream、BufferedOutputStream。先将读取/要写入的字节存放在缓存区,并从内部缓冲区中单独读取/写入字节。这样大幅减少了 IO 次数,提高了读取效率。缓冲区的大小默认为 8192 字节,当然了,你也可以通过 BufferedInputStream(InputStream in, int size) 这个构造方法来指定缓冲区的大小
InputStream/Reader: 所有的输入流的基类
OutputStream/Writer: 所有输出流的基类,
I/O 流为什么要分为字节流和字符流呢?
操作文本文件用字符流,操作二进制文件(图片视频声音)用字节流(字节流操作带中文的字符可能乱码)
字符编码和解码:
字符流:按指定的字符集处理字符编码和解码。在内部使用 InputStreamReader 和 OutputStreamWriter 类来桥接字节流和字符流的转换。
字节流:不会处理字符编码和解码,读写的是原始的字节数据。

内存泄漏和内存溢出?
内存泄漏:程序无法释放已占有的内存,多次内存泄漏会导致内存溢出。
内存溢出:程序申请的内存超出可用内存,所以报内存溢出错误。

如何防止内存泄漏:1使用软引用或弱引用,当内存不足时释放这些引用所指向的对象 2一定要让程序各个分支完整地执行到程序结束,然后看看某个对象是否被引用过,若没有引用过,则说明发生了内存泄漏。(软弱引用不足释放,分支执行到结束看某对象是否被引用过若没有则属于内存泄漏)

如何防止内存溢出: 1把JVM参数的可用内存改大 2检查错误日志,查找OOM前是否有异常或错误 3对代码进行走查和分析,找出会发生内存溢出的位置 4使用内存查看工具,动态查看内存使用情况。(改参,检查OOM前,走查分析找溢出,内存查看工具看内存情况)

InputStream、OutputStream、Scanner、PrintWriter等的资源都需要我们调用close()方法来手动关闭。多个资源需要关闭的时候,使用 try-with-resources 实现不出问题
用日志打印异常之后就不要再抛出异常。
用泛型(自动转换类型Object) :泛型类(实现泛型类,必需指定类型)、泛型接口(实现泛型接口,可指定可不指定类型)、泛型方法。
反射:在运行时获取调用任意一个类的所有属性和方法。反射在运行时为@Component注解的类创建 Spring Bean

输入和输出。入(读入)内存,出(写出)内存到外部存储(比如数据库,文件,远程主机)。不知道编码类型的话,使用字节流的过程中很容易出现乱码

jvm

jvm内存结构
分两类线程私有的和线程共享的,线程私有的:虚拟机栈(生命周期和线程相同,调方法产生栈帧:局部变量表、操作数栈、方法返回地址、动态链接)、本地方法栈(类似虚拟机栈,只不过调的是本地(C++)方法)、程序计数器 。
线程共享的:堆(虚拟机启动时创建,分新生代、老生代)、元空间(元空间在本地内存里, 存Class 文件类信息、字段信息、方法信息、常量、静态变量)

java7和8堆的区别

7堆中还有个永久代也叫方法区,来存类信息、常量、静态变量、编译后的代码。8为降低oom概率删除了永久代,把数据存储到了本地内存的元空间。

判断对象是否为垃圾的两种方式
引用计数法和可达性分析法,一个对象被引用了一次,他的引用次数就加一, 当引用次数为0时代表这个对象可回收,但是当对象出现循环引入时,也就是在类里面的属性值互相引用时,引用计数法会失效,所以引入了可达性分析算法来判断哪些内容时垃圾,过程是扫描堆中的对象,看是否能沿着gcroot对象为起点的一个引用链找到该对象,找不到该对象表示可以回收,虚拟机栈 (栈帧中的本地变量表)中的引用对象-方法中的局部变量,方法区中类静态属性引用的对象-类的静态成员变量,方法区中常量引用的对象本地方法栈中native方法引用的对象-类的加final的成员变量

垃圾回收(GC)机制
垃圾回收可防止内存泄漏进而防止内存溢出,每隔固定的周期去清理和回收堆中已死亡或长时间没用的对象。堆分年轻代和老年代两块,年轻代又分为三部分,Eden区和两个大小相同的幸存区S0和S1, Eden 区的对象经历一次垃圾回收后,还存活就进入 幸存区并且年龄加 1。在幸存区年龄到 15 岁,进入老年代。堆这里最容易出现OutOfMemoryError。当没有引用指向对象就可回收

垃圾回收算法:
1、标记-清除:用可达性分析算法标记存活的对象,然后回收没被标记的对象。会产生大量不连续的内存碎片。
2、标记-整理:比“标记-清除”多一步,整理存活的对象,把存活的对象移动到一端,然后清理掉端边界以外的内存,解决了内存碎片化。
2、复制:把内存分为两半,使用其中一半,垃圾回收时把存活的对象复制到另一半中,回收掉原来那一半的对象,效率高,无碎片,内存使用率低才一半

因为新生代中,存活的对象少,垃圾对象多,所以用”复制“算法,复制工作量小。而老年代没有额外的内存空间,并且对象存活几率高,所以用“标记-清除”或“标记-整理”算法回收垃圾。

垃圾回收器:
Serial和Serial Old,串行垃圾收集器,单线程回收垃圾。并且垃圾收集时,其他所有的工作线程会暂停。Serial 作用于新生代,采用复制算法,Serial Old 作用于老年代,采用标记-整理算法
Parallel New和Parallel Old,多线程并行回收垃圾,但收集垃圾时,其他所有的工作线程暂停,Parallel New作用于新生代,采用复制算法。Parallel Old作用于老年代,采用标记整理算法。JDK8默认使用此垃圾回收器,
CMS全称 Concurrent Mark Sweep,是一款并发的、使用标记-清除算法,该回收器是针对老年代垃圾回收的,垃圾回收时,应用仍然能正常运行。

垃圾回收时,多个线程在工作,并且java应用中的所有线程都要暂停(STW),等待垃圾回收的完成

类加载器的作用和分类:
把字节码文件加载到jvm运行时数据区。
启动类加载器:顶层的,用来加载 JDK的核心类库里的类(JAVA HOME/jre/lib)
扩展类加载器:加载(JAVA HOME/jre/lib/ext)目录和系统变量所指定的路径下的扩展类库
应用程序类加载器:加载java应用目录下的classpath 目录下的所有 jar 包和类。

​ 自定义类加载器:自定义类继承ClassLoader,实现自定义类加载规则

类加载器的双亲委派机制:
类加载器会把加载一个类的任务先委派给其父加载器,一直到顶级的启动类加载器。如果启动类加载无法加载,才会让他的子加载器尝试加载。这样确保了从上到下加载类,能防类被重复加载,篡改jdk的类库

JVM 调优的参数可以在哪里设置参数值?
项目打成war包部署在linux上的TOMCAT,在bin包下的catalina.sh里设置`JAVA_OPTS="-Xms512m -Xmx1024m"
在linux系统下启动springboot项目时,直接加参数 nohup java -Xms512m -Xmx1024m -jar xxxx.jar --spring.profiles.active=prod &
nohup和& : 用于在系统后台不挂断地运行命令,退出终端不会影响程序的运行
JVM 调优的参数都有哪些
-Xms:设置堆的初始化大小
-Xmx:设置堆的最大大小

系统CPU持续飙高的话
top命令查看占用cpu的进程排行,
ps H -eo pid,tid,%cpu grep 2266查看进程id的所有线程占用cpu,把线程id转16进制
jstack 进程id 打印 JVM进程线程的堆栈运行时报错信息,根据线程id转16进制 定位线程

先排查代码问题,则咨询运维或者云服务器供应商,通常服务器重启或者服务器迁移即可解决。

JVM 调优的工具
jdk目录的 bin目录下,启动 jconsole.exe 就行,看内存、线程

 jps(Java Process Status),查看JVM进程id(现在一般使用jconsole)
jstack   JVM进程id,      JVM进程线程的堆栈运行时报错信息

排查线上问题
看日志,远程debug

tail [-n 100] -f 日志文件   实时查看日志[最后100行]    ,number of lines(行数),follow(跟随)
grep -C 50“关键字”文件名 ,可以显示含关键字行及其前后 50 行

grep -C 10“EROR”文件名
cat -n er.log| grep "ERROR"
grep -n "ERROR" 日志文件可以显示含关键字行和行号
cat -n 日志文件 l tail-n +100|head -n 100(从100行开始查100行的日志)

快速定位系统性能瓶颈问题
用Jmeter给接口做压测看响应速度

==================================赵文梦

JVM由哪些部分组成,运行流程是什么?
类加载器(ClassLoader),运行时数据区(Runtime Data Area,内存分区。包括元空间,堆,虚拟机栈,本地方法栈),执行引擎(Execution Engine),本地库 接口(Native Method Library)
运行流程是类加载器把Java代码转换为字节码,运行时数据区把字节码加载到内存中,执行引擎翻译字节码为系统指令,交给CPU执行,执行中需要调c++的本地库接 口。

你能给我详细的介绍Java堆吗?
	堆是线程共享的区域,用来存对象实例和数组,当堆中没有内存空间存储对象实例,则抛出内存溢出OutOfMemoryError异常。
	堆分年轻代和老年代两块,年轻代又分为三部分,Eden区和两个大小相同的幸存区,经过几次gc后,幸存区里还存活的对象被移到老年代。老年代存生命周期长		的对	象,一般是一些老的对象。java7还有一个永久代存类信息,静态变量,常量(运行时常量池),和编译后的代码。java8移除了永久代,将这些数据存到了		本地内	存的	元空间,防止内存溢出。


什么是虚拟机栈
	每个线程运行时占用的内存,因为是栈所以先进后出。每个栈由多个栈帧组成,每次方法调用产生一个栈帧压入栈,执行完弹栈。
		方法内的局部变量是否线程安全?
			局部变量在方法内定义的,线程安全
			局部变量是引用类型,引用了方法外定义的对象,线程不安全
		垃圾回收是否涉及栈内存?
			垃圾回收主要指就是堆内存,当栈帧弹栈以后,内存就会释放
		栈内存分配越大越好吗?
			机器总内存/栈内存=可同时运行的线程数,大了,可同时运行的线程数少
		栈内存溢出
			无限递归调用,导致栈帧过多导致栈内存溢出

.栈(stack)和堆(heap)的区别
栈存函数里的基本类型的变量和对象的引用变量,空间小,速度快,储存空间是连续的。
堆存函数创建的对象,是手动new申请开辟,获得的空间大,但是速 度会比较,储存空间是不连续的。

jvm内存结构
主要是分为两类,线程私有的和线程共享的,线程私有的包括虚拟机栈、本地方法栈和程序计数器,虚拟机栈就是每个线程运行时需要的内存。因为他是栈 ,所以是先进后出的,由多个栈帧组成,对应着每次方法调用所占用的内存,每个线程只有一个活动栈帧,对应着正在执行的方法 大量递归调用会导致栈内存溢出。本地方法栈 :与虚拟机栈相似,区别在于本地方法栈调用本地的native方法,由c/c++编写。当调用native方法时,会使用本地方法栈来执行这个方法。程序计数器 :1个线程一个 指向正在执行的字节码文件的下一条指令

线程共享
方法区/元空间:存类的信息(静态变量,常量(运行时常量池),和编译后的代码),jvm启动时创建,关闭时释放。
jdk1.7堆里有个永久代存类的信息(静态变量,常量(运行时常量池),和编译后的代码),在jdk1.8移除了永久代,将这些数据存到了本地内存的元空间,来 防止 内存溢出。
堆 :用来存储对象实例和数组等(字符串常量池就在里面),它包括年轻代(伊甸园区和两个幸存者区)和老年代,我们jvm的gc主要对堆的垃圾回收。

双亲委派机制
类加载器从上到下有,启动类加载器,扩展类加载器,应用类加载器,自定义类加载器。类加载器作用是将字节码文件加载到jvm中,就是当收到了一个类的加载请求,他不会自己去加载这个类,而是先把这个请求委托给父类加载器去完成,每一个层次类加载器都是如此。直到最顶层的启动类加载器,就是说他自底向上查找是否加载过这个类,如果加载过就直接返回,一直到顶然后自顶向下进行加载;作用时确保核心的安全性和完整性,避免一个类被重复加载。自上向下谁能加载就直接加载,上面的优先级高,由上面的类加载器先加载。

实现自定义类加载器,重写defineClass方法,将双亲委派机制的代码去除,来打破双亲委派机制

初始化类的时候优先

父类静态代码块
子类静态代码块
父类构造代码块
父类构造函数
子类构造代码块
子类构造函数

静态变量和静态代码块,则按照自上而下的顺序依次执行

垃圾回收算法
在讲垃圾回收之前,需要判断哪些对象时垃圾,有两种方式来确定,一个是引用计数法,一个是可达性分析算法,一个对象被引用了一次,他的引用次数就加一, 当引用次数为0时代表这个对象可回收,但是当对象出现循环引入时,也就是在类里面的属性值互相引用时,引用计数法会失效,所以还有一个算法时可达性分析算法来判断哪些内容时垃圾,过程是扫描堆中的对象,看是否能沿着gcroot对象为起点的一个引用链找到该对象,找不到,表示可以回收,虚拟机栈 (栈帧中的本地变量表)中的引用对象,方法区中类静态属性引用的对象,方法区中常量引用的对象 本地方法栈中native方法引用的对象

标记清除算法:分为两个阶段标记和清除,首先根据可达性分析算法垃圾,然后清除 速度快,内存碎片化严重。

复制算法:讲内存分为两半,使用其中一半,然后讲不是垃圾的对象复制到另一半中,之前使用的一般全部回收掉,效率高,无碎片,内存使用率低

标记整理算法:在标记清除算法的基础上加了一步整理,解决了标记清除算法内存碎片化严重的问题

JVM的分代回收算法:将内存分为新生代和老年代 内存比为1:2

新生代分为伊甸园区两个幸存者区,伊甸园区存储新创建的对象,两个幸存者区分为相同的from 和 to两块 8:1:1

伊甸园内存不足时,垃圾回收时将伊甸园区和幸存者区的from 使用复制算法存入to中,释放伊甸园区和from区的内存,经过一段时间 伊甸园区满了,标记伊甸园区和to区的对象,放入到from区,to和伊甸园区清空。幸存过15次的对象或者幸存区内存不足,或大对象,会晋升到老年代

minorgc也叫younggc 发生在新生代stw 暂停时间短 ,mixedgc新生代加老年代部分区域,g1收集器 fullgc新生代加老年代完整垃圾回收,暂停时间长stw应尽量避免。

CMS G1工作原理
串行垃圾收集器:单线程垃圾回收,堆内存小,适合个人电脑 Serial 作用于新生代,采用复制算法,Serial作用于老年代,采用标记整理算法,垃圾回收时只有一个线程工作,其他线程都要停止stw

并行垃圾收集器:Parallel New作用于新生代,Parallel Old作用于老年代,与串行垃圾收集器不同的是,垃圾回收时多个线程同时区回收垃圾,并且所以线程都要stw。等待垃圾回收的完成。

CMS垃圾收集器:并发的,标记清除算法的垃圾收集器,针对老年代的垃圾回收,时间停顿短,用户体验好,最大特点时垃圾回收时,仍然可以正常运行,初始标记 其他线程阻塞,,并发标记 不阻塞,重新标记的时候其他线程不运行,最后并发清理, jdk8默认

G1垃圾收集器;作用于新生代和老年代时jdk9默认的垃圾收集器,划分多个区域,每个区域都可以充当,伊甸园区,幸存者区,老年代,和大对象区,采用的是复制算法,响应时间和吞吐量兼顾,分为三个阶段,新生代回收 并发标记 混合收集,

如果回收速度赶不上创建新对象的速度,触发fullgc。

年轻代垃圾回收,复制算法 暂停用户线程。。。 当老年代占用内存超过阈值45%之后,触发并发标记,不暂停用户线程,然后时重新标记阶段,需要暂停用户线程,然后进入混合收集阶段,根据暂停时间,优先回收存活对象少的区域,不会全部回收老年代区域。 进入下一轮,

对象的强软弱虚引用
强引用:new出来的是强引用,即使内存不足抛出OOM错误也不回收。
软引用:内存快溢出,才会被gc回收
弱引用:遇到gc就回收掉该弱引用。
虚引用:随时都可能被回收,(不能单独使用,需要配合引用队列使用,主要作用时跟踪对象被垃圾回收的状态)

标签:java,对象,引用,线程,内存,JVM,集合,方法,加载
From: https://www.cnblogs.com/zyzyzyBlog/p/18655290

相关文章

  • 古玩玉器交易系统|Java|SSM|VUE| 前后端分离
                 【技术栈】1⃣️:架构:B/S、MVC2⃣️:系统环境:Windowsh/Mac3⃣️:开发环境:IDEA、JDK1.8、Maven、Mysql5.7+4⃣️:技术栈:Java、Mysql、SSM、Mybatis-Plus、VUE、jquery,html5⃣️数据库可视化工具:navicat6⃣️服务器:SpringBoot自带apachetomcat......
  • 固定资产管理系统|Java|SSM|VUE| 前后端分离
                 【技术栈】1⃣️:架构:B/S、MVC2⃣️:系统环境:Windowsh/Mac3⃣️:开发环境:IDEA、JDK1.8、Maven、Mysql5.7+4⃣️:技术栈:Java、Mysql、SSM、Mybatis-Plus、VUE、jquery,html5⃣️数据库可视化工具:navicat6⃣️服务器:SpringBoot自带apachetomcat......
  • 共享充电宝系统|Java|SSM|VUE| 前后端分离
                 【技术栈】1⃣️:架构:B/S、MVC2⃣️:系统环境:Windowsh/Mac3⃣️:开发环境:IDEA、JDK1.8、Maven、Mysql5.7+4⃣️:技术栈:Java、Mysql、SSM、Mybatis-Plus、VUE、jquery,html5⃣️数据库可视化工具:navicat6⃣️服务器:SpringBoot自带apachetomcat......
  • 个性化电影推荐系统|Java|SSM|JSP|
                 【技术栈】1⃣️:架构:B/S、MVC2⃣️:系统环境:Windowsh/Mac3⃣️:开发环境:IDEA、JDK1.8、Maven、Mysql5.7+4⃣️:技术栈:Java、Mysql、SSM、Mybatis-Plus、JSP、jquery,html5⃣️数据库可视化工具:navicat6⃣️服务器:SpringBoot自带apachetomcat......
  • 描述一下 JVM 加载 Class 文件的原理机制
    JVM加载Class文件的原理机制涉及多个步骤和组件,主要包括类加载器(ClassLoader)和类加载过程。下面详细描述这些机制:1.类加载器(ClassLoader)类加载器是JVM中负责加载类的组件。JVM提供了三种主要的类加载器:启动类加载器(BootstrapClassLoader):负责加载Java核心库(如......
  • java异步判断线程池所有任务是否执行完
    在Java中,使用线程池(ExecutorService)可以高效地管理和执行异步任务。对于某些应用场景,可能需要异步地判断线程池中所有任务是否执行完毕。以下是一个高度专业的指南,讲解如何在Java中实现这一功能。步骤概述创建并配置线程池。提交多个异步任务到线程池。使用 CompletionServi......
  • 咱们一起学 Java(145)
    咱们一起学Java(145)在之前对Java应用程序部署相关知识的学习中,我们已经掌握了JAR文件的创建与使用,以及资源管理和密封机制等重要内容。今天,我们将把重点放在应用程序首选项的存储上,这是提升用户体验的关键环节。用户在使用应用程序时,通常希望能够保存自己的个性化设置和偏......
  • 咱们一起学 Java(144)
    咱们一起学Java(144)在之前对Java应用程序部署中JAR文件的学习里,我们已经了解了如何将应用程序打包成一个方便分发的文件格式。今天,我们将继续深入探讨部署过程中的另外两个重要方面:资源管理和密封。资源管理涉及到如何在应用程序中有效地组织和访问各种数据文件,如图片、文......
  • 咱们一起学 Java(143)
    咱们一起学Java(143)在之前的Java学习旅程中,我们专注于掌握Java语言的特性以及图形编程知识,努力打造功能强大的应用程序。但当应用程序开发完成后,我们面临着一个关键问题:如何将其有效地部署到用户的计算机上?这就引出了我们今天要深入探讨的主题——JAR文件。JAR文件在Java应......
  • 咱们一起学 Java(142)
    咱们一起学Java(142)在之前的学习中,我们已经对Swing程序的调试技巧有了一定的了解,包括查看组件层次结构、使用图形化调试器以及事件跟踪器等。今天,我们将进一步深入实践,结合AWT机器人(Robot)的自动化测试功能,探讨如何更全面、有效地调试和测试Swing程序。通过实际的代码示例和......