JAVA基础
2024-01-11 13:43:54 0 举报
AI智能生成
JAVA基础是学习Java编程的第一步,它涵盖了Java的核心概念、语法和结构。在学习JAVA基础时,你将了解如何定义类、方法和变量,如何使用运算符,以及如何创建和控制流程。此外,你还将学习到面向对象编程的概念,如继承、封装和多态。在学习过程中,你将接触到诸如字符串、数组、集合和异常处理等重要主题。JAVA基础对于理解Java编程语言的关键概念至关重要,将为你进一步学习高级Java编程和框架打下坚实基础。
作者其他创作
大纲/内容
基础概念
特点
面向对象
平台无关性(基于java虚拟机实现)
编译与解释并存
javaSE
标准版
javaEE
企业版
企业级应用程序开发
部署的标准和规范
javaME
微型版
JDK
java SDK,java开发工具包,包含核心类库、JRE、JVM、JIT、javac、javadoc、jconsole等
JRE
java运行时环境
主要包括:JVM、核心类库
jdk9弃用,jdk11后不再支持。9后将核心类库拆分为64个模块,并新增jlink工具,运行时根据运行程序创建运行时映像,只携带需要的模块。简化 Java 应用的部署和节省内存并增强安全性和可维护性
JVM
一次编译,随处可以运行
常用:hotspot
java程序运行流程:源代码->javac编译->解释器&JIT->机器码运行
JIT
Just In Time编译器:即时编译器,运行时编译
热点字节码编译成机器码保存内存中,普通字节码解释运行。
分层编译
client编译器:C1编译器
server编译器:C2编译器
差别:编译时机不同,C1注重速度;C2注重代码优化
java8默认采用分层编译,即C1与C2结合。启动时用 client 编译器,然后随着代码变热使用server编译器。可通过JVM参数控制关闭分层编译
解释器转编译器时机:计数器
一段时间内某个方法的调用次数
超过时间次数减半,成为半衰周期,可通过JVM参数控制关闭和时长。
调用次数计数器:记录方法在一段时间内的调用次数
循环回边计数器:记录循环次数,循环一次记一次。达到预定次数采用编译器编译代码执行,这种编译称为栈上替换
AOT
jdk9推出了新的编译模式 AOT
执行前就将其编译成机器码,与C相同,属于静态编译
优点1:不容易被反编译和修改,提高程序安全性
优点2:打包体积更小、内存占用更少、程序启动更快
缺点:由于静态编译的缘故,不支持反射,动态代理等特性
基本语法
注释
单行注释:/内容/,用于解释单行代码的作用
多行注释:/*内容*/,用于解释一段代码的作用
文档注释:/**内容*/,用于生成 Java 开发文档
标识符
标识符就是一个名字,比如:类名、变量名、方法名等
关键字
关键字是被赋予特殊含义的标识符
自增自减运算符
自增:++,某个整数类型变量增加 1。++在前,先+1再赋值,++在后,先赋值再+1。
自减:--,某个整数类型变量减少1。--在前,先-1再赋值,--再后,先赋值再-1。
移位运算符
<<:左移
>>:右移
>>>:无符号右移
double、float比较特殊,不能进行移位操作。
对short、byte、char类型进行移位,编译器会将其转换为int
continue/break/return
continue: 跳过本次循环,执行下次循环。
break:跳出循环,结束循环。
return:跳出方法,结束方法,可返回【返回值】
基本数据类型
基本数据类型
byte,8位,1字节,-128(2^7)~127(2^7 - 1)
short,16位,2字节,-32768(-2^15)~32767(2^15 - 1)
int,32位,4字节,-2147483648 ~ 2147483647
long,64位,8字节,-9223372036854775808(-2^63) ~ 9223372036854775807(2^63 -1)
float,32位,4字节,1.4E-45 ~ 3.4028235E38
double,64位,8字节,4.9E-324 ~ 1.7976931348623157E308
char,16位,2字节,0 ~ 65535(2^16 - 1)
boolean,1位true、false
包装类
包装类可以用于泛型,基本数据类型不可以
包装类属于对象,一般存储在堆中
基本数据类型的局部变量存放在栈中,基本数据类型的类的成员变量存放在堆中
JIT存在优化代码的可能,JIT会对对象做逃逸分析,如果一个对象没有逃逸到方法外部,则可能通过标量替换来实现栈上分配。
相比于基本数据类型,包装类属于对象,需要占用更多空间
包装类默认值为null,基本数据类型都有自己的默认值
基本数据类型用==比较的是值,而包装类比较的是地址
包装类中,Byte、Short、Integer、Long在对象中都创建的有[-128~127]的缓存数据。
包装类中,Character创建了数值在[0~127]的缓存数据。
包装类中,Boolean创建了两个缓存数据:True和False。
Float和Double没有缓存数据
在使用包装类时,如果值的范围在对应的缓存数据范围内,则用==比较可以得到正确的结果,负责会因为堆中地址不同而造成比较失败。建议包装类比较都是用equals()
装箱与拆箱
装箱与拆箱就是基本数据类型与包装类实现互转
装箱实际上就是调用了对应包装类的valueOf()方法将基本数据类型的数据转换为包装类
拆箱实际上就是调用了xxxValue()方法,比如Integer的intValue()方法
浮点数精度问题
float和double在计算时会存在精度丢失问题,这是因为计算机的计算都是通过二进制进行计算的,而小数转换为二进制数据时,会存在无法转换,一直有余数的问题,导致被截断。
涉及到小数的精密计算,推荐使用BigDecimal。
数字超长问题
一个整数的长度比long类型的范围还要大,可以使用BigInteger,其原理是使用一个int数组来存储任意大小的整数
BigInteger运算的效率会相对较低
方法
方法的返回值 是指我们获取到的某个方法体中的代码执行后产生的结果
静态方法为什么不能调用非静态成员
加载时机不同:静态方法属于类,在类加载时就会分配内存。非静态成员属于对象,只有对象实例化后才存在,需要通过实例化对象才能访问。
静态方法存在时非静态成员还不存在,此时调用在内存中还不存在的非静态成员,属于非法操作
静态方法和实例方法的区别
1.调用方式不同:静态方法可以通过类名.方法名调用,也可以通过对象.方法名调用。实例方法只能通过对象.方法名调用。 一般建议使用类名.方法名调用静态方法,因为静态方法属于类,使用对象.方法名调用容易混淆。
2.访问权限不同,静态方法只能访问静态成员(即静态成员变量和静态方法),不允许访问成员变量和成员方法。而实例方法不存在这个限制。
重载
重载:同一个方法根据不同的数据做出不同的响应或处理。
方法名必须相同,参数类型、参数数量、参数顺序不同,返回值、修饰符可以不同。
重载也可以发生在子类与父类之间
重写
重写:子类继承父类的方法,输入数据一样,但需要做出不同的处理时,覆盖父类方法重新编写处理代码。
两同两小一大:方法名、参数列表必须相同,返回值类型小于等于父类方法返回值,声明抛出异常类型小于等于父类异常,修饰符大于等于父类修饰符。
如果方法的返回类型是 void 和基本数据类型,则返回值重写时不可修改。
如果方法的返回值是引用类型,重写时是可以返回该引用类型的子类的
构造方法无法被重写
可变长参数
允许在调用方法时传入不定长度的参数
遇到方法重载的情况,会优先匹配固定参数的方法,因为固定参数的方法匹配度更高。
变量
成员变量
成员变量属于类
存储在堆中
随着对象的存在而存在
基本数据类型的成员变量有其对应默认值,其他类型的成员变量 默认为null
成员变量的值可以在运行中赋予,如果没有赋值且没有默认值,JVM读取该值运行时会出现意外,因此程序必须自动给定默认值。
局部变量
定义在代码块或方法中的变量
基本数据类型的局部变量存在栈中,对象的变量名和地址存在栈中,值存在堆中
可以没有默认值,但在执行时一定会赋值。且程序不会自动给默认值。
局部变量在定义时就能看出是否有值。如果没有赋值,编译时就能发现并给出报错。因此不需要自动给默认值
随着方法的调用而创建,随着方法的结束而销毁
类变量
类中使用static修饰的变量,又叫静态变量
类变量归类所有,所有实例共享,通过类名访问
静态常量
类中使用final修饰的属性被称为常量,final修饰的常量是实例级别的,每次创建实例都会创建,不符合常量的理念。
static和final修饰的属性被称为静态常量,静态常量是属于类的,多个实例共享使用同一个静态常量,只占用一个内存空间。
面向对象
将具有相同特征的事物或功能抽象为一个类,通过声明类的对象并调用对象.方法的方式解决问题。
易维护,易复用,易扩展。
用new关键字创建对象,用对象引用指向对象实例。对象引用存在栈内存,对象实例存在堆内存。对象引用存的是对象实例在堆中的地址。
创建类时程序会默认一个无参构造,重载会覆盖。
构造方法名必须与类名相同,构造方法没有返回值,且不能用void修饰。构造方法不能被子类重写,可以被重载。
面向对象三大特征
封装
对象属性私有化,不直接暴露给外部,但可以提供方法来访问。
继承
一个类可以继承另一个类,子类可以拥有和访问父类的非私有方法和属性。子类拥有父类的私有属性但不能访问。
多态
父类引用子类对象
抽象类与接口
抽象类和接口都有抽象方法,jdk8都可以使用default对抽象方法做默认实现。
接口主要定义约束和规范,抽象类主要用于代码复用
一个类只能继承一个类,可以实现多个接口。
接口中不能定义属性,只能定义静态常量和抽象方法。
深拷贝与浅拷贝
浅拷贝会在堆上创建一个新的对象,但对象中的属性如果还是对象,则不会拷贝,而是直接引用旧的
深拷贝完全复制整个对象,包括这个对象所包含的内部对象。
jdk提供的拷贝方式需要实现Cloneable接口并重写clone()方法。
也可以通过json工具实现深拷贝。
spring的BeanUtils使用的是浅拷贝,Hutool的BeanUtil使用的是深拷贝
Object
Object类中主要提供getClass()、hashCode()、equals()、wait()、nodify()、finalize()、toString()等方法
== 和 equals() 的区别
对于基本数据类型,==比较的是值
对于引用数据类型,==比较的是对象的内存地址
equals只能用于判断引用数据类型
对于未重写equals方法的类,equals比较等价于==
hashCode
hashCode也是判断两个对象是否相等的
hashCode主要用在一些如HashMap之类的容器中,比较两个对象是否相等的效率会更高
重写equals必须重写hashCode,否则会出现equals比较时相等,hashCode不一致问题。
String
String是不可变的
保存String用的是char数组,且被final和private修饰,并且没有暴露和提供任何修改的方法
String类被final修饰,不可被继承,避免了子类破坏String不可变
String使用+拼接,实际上是在编译期替换为StringBuilder进行append操作
循环拼接时java不会在循环外创建StringBuilder并复用,而是循环内不断创建StringBuilder进行拼接,会导致内存和性能浪费
JDK9优化了+拼接的方式,使用动态方法makeConcatWithConstants()实现
JDK9为什么将保存String的数组改为byte数组
byte占一个字节,char占两个字节,使用byte可以节省一半内存
新版的String使用两种编码方案,ISO-8859-1(Latin-1)和UTF-16,如果字符串中的字符都是Latin-1范围内的字符,则使用Latin-1作为编码格式,能有效节省内存
JDK官方解释,绝大部分字符串对象只包含 Latin-1 可表示的字符
UTF-16采用固定长度的编码方式,每个字符固定为2或4个字节。更适合处理非ASCII字符
StringBuilder和StringBuffer
StringBuilde和StringBuffer都继承自AbsractStringBuilder, 底层采用非private非final的字符数组保存字符串,因此可操作。
StringBuffer使用同步锁synchronized保证线程安全,String由于其不可变特性,因此也是线程安全的。
少量拼接用+
大量拼接且线程安全用StringBuffer
大量拼接不要求线程安全用StringBuilder
String 中的 equals 方法是被重写过的,比较的是 String 字符串的值是否相等。 Object 的 equals 方法是比较的对象的内存地址。
String s = new String('abc') 会在堆中创建一个或两个对象。
字符串常量池中不存在字符串对象“abc”的引用,那么它会在堆上创建两个字符串对象,其中一个字符串对象的引用会被保存在字符串常量池中。
如果字符串常量池中已存在字符串对象“abc”的引用,则只会在堆中创建 1 个字符串对象“abc”。
常量折叠
编译期可以确定值的字符串,也就是常量字符串 ,jvm 会将其存入字符串常量池。并且,字符串常量拼接得到的字符串常量在编译阶段就已经被存放字符串常量池,这个得益于编译器的优化,如String str3 = "str" + "ing"; 编译器会给你优化成 String str3 = "string";
程序编译期就可以确定值的常量才会发生常量折叠
基本数据类型( byte、boolean、short、char、int、float、long、double)以及字符串常量。
final修饰的基本数据类型和字符串变量
常量或final字符串通过 “+”拼接得到的字符串、常量或final基本数据类型之间算数运算(加减乘除)、位运算(<<、>>、>>> )
异常
Throwable
java异常的基类,Exception和Error都继承自Throwable
Exception
Checked Exception
受检查异常是在编码过程中就需要处理的异常,如果没有被 catch或者throws 关键字处理的话,就没办法通过编译。
Unchecked Exception
RuntimeException 及其子类都统称为非受检查异常
Error
Error 属于程序无法处理的错误,如 Java 虚拟机运行错误(Virtual MachineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。
泛型
JDK 5 中引入的一个新特性
增强代码的可读性以及稳定性,减少强制转换
三种使用方式
泛型类
泛型接口
泛型方法
反射
在运行时分析类以及执行类中方法和属性的能力
让代码更加灵活
增加安全问题,比如可以跳过编译时的泛型安全检查
性能相对较差
注解
Java5 开始引入的新特性
主要用于修饰类、方法或者变量,提供某些信息供程序在编译或者运行时使用
声明注解的方式
1.使用@interface声明
2.注解类上标记元注解
@Target
用于指定其他注解的应用目标,常用的有类、方法、字段
@Retention
用于控制注解的生命周期,分别为只保留在源文件、保留在Class文件、运行时加载
SPI
SPI是服务提供者提供的可供扩展的接口,调用者使用时需要基于接口和规范做实现。
序列化
序列化:将数据结构或对象转换成二进制字节流的过程
反序列化:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程
使用场景
网络传输
持久化对象
序列化的主要目的是通过网络传输对象或者说是将对象存储到文件系统、数据库、内存中。
序列化协议属于 TCP/IP 协议应用层的一部分
transient 关键字
对于不想进行序列化的变量,使用 transient 关键字修饰
阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。
注意事项
transient 只能修饰变量,不能修饰类和方法
transient 修饰的变量,在反序列化后变量值将会被置成类型的默认值。例如,如果是修饰 int 类型,那么反序列后结果就是 0。
static 变量因为不属于任何对象(Object),所以无论有没有 transient 关键字修饰,均不会被序列化。
常见序列化协议
Kryo
Protobuf
不推荐使用 JDK 自带的序列化
不支持跨语言调用
性能差,主要原因是序列化之后的字节数组体积较大,导致传输成本加大
存在安全问题:序列化和反序列化本身并不存在问题。但当输入的反序列化的数据可被用户控制,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码。
IO
IO 即 Input/Output,输入和输出。数据输入到计算机内存的过程即输入,反之输出到外部存储(比如数据库,文件,远程主机)的过程即输出。数据传输过程类似于水流,因此称为 IO 流。IO 流在 Java 中分为输入流和输出流,而根据数据的处理方式又分为字节流和字符流。
I/O 流为什么要分为字节流和字符流
字符流是由 Java 虚拟机将字节转换得到的,这个过程还算是比较耗时;
如果我们不知道编码类型的话,使用字节流的过程中很容易出现乱码问题。
0 条评论
下一页