JavaSE 笔记
一、Java开发环境搭建
1、Java语言的特性
- 简单性 : 相比C 和 C++来说,是简单的!
- 健壮性 : 存在垃圾回收(GC)机制 供JVM调用,自动释放不用的内存
- 可移植性、跨平台 :
优点:一次编写到处运行 缺点:麻烦,运行Java程序必须现有一个JVM
2、JDK、JRE、JVM
-
JDK(Java Developer’s Kit): Java开发工具箱
-
JRE(Java Runtime Environment): Java运行时环境
-
JVM(Java Virtual Machine): Java虚拟机 (注意:JDK 包括 JRE 包括 JVM)
3、Java的加载与执行
注意:源代码(xxx.java文件)不参与执行,参与执行的是编译执行后生成的字节码文件(xxx.class文件)!
两个阶段,可以在不同的操作系统上执行
JVM会把字节码文件解释为相应的二进制文件供操作系统识别
4、JDK的下载
官网:https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html
下载完成后,双击执行.exe文件下一步即可
注意:如果是JDK13,仅仅是内置一个JRE,如果是JDK8会再生成一个JRE!
5、环境变量 path
path 环境变量就是给 windows操作系统指路的
默认情况下,在Windows操作系统的Dos窗口下,windows只会在当前Dos打开的目录,和path指定的路径下去找命令!
其他环境配置 :
- JAVA_HOME 环境变量 :设置jdk的安装路径、抽取目录,相当于为一串路径起个别名!
- classpath 环境变量 :让为我们的ClassLoader(类加载器)指明方向,让我们 java + 类名运行字节码文件时,类加载器不仅仅只在当硬盘中的当前目录下加载字节码文件,可以自定义类加载器的寻找路径!
- Java程序在运行时,首先会启动JVM(Java虚拟机),然后JVM会去启动“类加载器classloader”负责去硬盘上找“类”对应的“字节码”文件。默认情况下类加载器(classloader)会从当前路径下找。
- 2.classpath环境变量的作用是给类加载器指路的。
在没有配置环境变量classpath的时候,默认从当前路径下加载。
如果配置了环境变量classpath的话,就只能从指定的路径下加载类。 - 3.classpath环境变量不属于windows操作系统,classpath环境变量隶属于java。
classpath环境变量是java特有的。 - 使用Idea运行字节码文件的时候默认是在字节码同一目录,所以能找到!
JDK中rt.jar、tools.jar和dt.jar作用
-
rt.jar :
这个文件是极为重要的一个文件,rt是runtime的缩写,即运行时的意思。是java程序在运行时必不可少的文件。
里面包含了java程序员常用的包,如java.lang,java.util,java.io,java.net,java.applet等,默认就在Root ClassLoader的加载路径里面 -
tools.jar
系统用来编译一个类的时候用到的 也就是javac的时候用到
-
dt.jar
dt.jar是关于运行环境的类库,主要是swing的包 你要用到swing时最好加上
JDK13新特性:直接java + Hello.java就可以运行,也不会生成Class字节码文件【了解即可!】
6、Java中的注释
- 方式一:// 注释内容 ,单行注释
- 方式二:/* 注释内容 */ ,多行注释
- 方式三:/** 注释内容 */ ,Javadoc注释,这里的注释会被javadoc.exe命令解析提取出来,并生成到帮助文档当中!
命令:Javadoc -d 指定生成目录 xxx.java
【了解即可】
7、Public Class 和 Class
一个XXX.java文件能有多个Class,可以没有Public Class,【得出一个类就是一个字节码文件、体现出一个java文件可以生成多个字节码文件!】
一个xxx.java文件如果有且只能有一个被public修饰的类,并且与文件同名!
一个Class只能有一个入口函数 public static void main(string[] args){ }
二、标识符与关键字
标识符可以标识:类名、方法名、接口名、常量名、变量名 …
1.标识符命名规则
- 只能由 数字、字母、下划线_ 、美元符号$ 、构成
- 不能数字开头,严格区分大小写、不能含有空格、关键字不可作为标识符
- 理论上无长度限制
2.标识符命名规范
规则与规范的区别:规则相当于法律,规范相当于道德! 不可违背法律,但是最好遵守道德!
- 最好见名知意
- 遵守驼峰命名方式
- 类名、接口名首字母大写,后面每个单词首字母大写 例: BlogInputController{ }
- 变量名、方法名 首字母小写,后面每个单词的首字母大写 例 : addBlog( )
- 常量名 全部大写,单词间用下划线_隔开 例 : MATH_PATH = 3.1415926
3.关键字
SUN公司开发Java语言的时候,提前定义好的一些具有特殊含义的单词!例如:public、class 、static 、void 等
3.1、static关键字
static可以修饰方法、变量【static所修饰的即为类级别的!】
//举例:
class StaticTest{
//以下变量和方法都属于实例变量/方法,访问需要"引用."的方式,需要new对象访问!
int age ;
public void setAge(){ };
//以下变量和方法都属于静态变量/方法,访问直接"类名."的方式即可!
static int name ;
public static void setName(){ };
}
静态变量的用途:一般用于对象的某个属性不会改变,例如static string country = “中国” ;会直接在类加载的时候在方法区中初始化,且只保留一份,不会浪费堆内存,不需要new对象就有值,因此构造方法,也不用添加该属性,类似默认值!
静态代码块
静态代码块,在类加载时执行,且只执行一次,并且在main方法之前执行,【一般在静态代码块中记录一下日志,了解即可】
//如果有多个静态代码块,自上而下顺序执行!
static {
System.out.println("静态代码块 01!")
}
static {
System.out.println("静态代码块 02!")
}
//测试:静态代码块和静态变量的执行顺序!
class StaticTest{
//静态变量,类记载时被初始化!
static int age = 20 ;
//静态代码块,类加载时执行!
static {
Syste.out,println(""age = "+ age); //因为都是类加载时执行,所以可以访问到,前提:还得是静态变量定义在代码块之前!
}
//入口函数
public static void main(){
}
}
3.2、this关键字
this是一个变量、是一个引用,this保存当前对象的内存地址,指向自身,且this存储在堆内存中,对象的内部
**this使用在实例方法中,代表当前对象!**常用于set、get方法当中,也因此set,get方法为实例方法,!
如果方法中可以直接访问实例变量,那么该方法一定是实例方法!
注意:常常可以省略,但是在set方法中一般不省略!为的是避免参数和属性重复,导致set失败!
this() 使用在构造方法中
语法:this(实际参数列表) ,构造方法调用构造方法
class Data{
int year ;
int month ;
int day;
public Data(){
this(year,month,day); //一般用于无参构造赋初值,且只能出现在构造方法的第一行,必须是第一个语句!
}
public Data(int year,int month,int day){
this.year = year ;
this.month = month ;
this.day = day ;
}
}
好处:代码复用,减少代码量!
3.3、super关键字
与this做对比学习!
结论:当一个构造方法的第一行,既没有this也没有super,默认会有一个super( ),表示当前子类构造方法,调用父类的无参构造方法,所以,必须保证父类的无参构造方法是存在的!
//super测试
public class Test{
public static void main(string[] args){
new B(); //输出:构造A 构造B
}
}
class A{
public A(){System.out.println("构造A")}
}
class B exdents A {
public B(){
super(); //这个super一般省略,等价于new A ( ),先初始化父亲,才能new儿子!
System.out.println("构造B")}
}
super的JVM内存图
注意:虽说new一个对象一直在调用父类的构造方法!其本质还是new的一个对象,只是将父类的特征继承过来!super( )可以看作,初始化当前对象的夫类型特征;
3.4、super与this的区别:
this:
- this只能出现在实例方法中
- 两种语法:this. 和 this ( )
- this在大部分情况下可以省略,但是在区分局部变量和实例变量时,不能省略this
- this( )只能出现在构造方法第一行,表示调用别的构造方法!
super:
- super能出现在实例和构造方法中
- 两种语法:super. 和 super()
- 不能使用在静态方法中
- super在大部分情况可以省略,但是
- super ( ) 同样只能出现在构造方法第一行,表示通过当前子类构造方法去调用父类的构造方法!模拟的是,要想有儿子,就要先有父亲的原则、目的是:表示在初始化子类对象的时候,先初始化父类对象特征!
结论:
- this()和 super( )不能共存!
- this()调用同类其他构造
- super()调用父类构造方法
- super . 表示当前对象的父类型特征的 后可以加属性或方法 表示访问父类的属性或方法、 this. 表示当前对象的
- 默认是无参,()中通过参数列表判断执行那个构造方法!
- 在子类无法访问父类的私有的方法,没有权限!
3.5、final关键字
final + 类 :final修饰的类无法被继承 ;
final + 方法 : final修饰的方法无法被覆盖(无法重写);
final + 局部变量 : final修饰局部变量一旦赋值,不可修改 【只能赋一次值】 ;
final + 引用 :引用指向的堆内存不可修改,队形不可改,但是对象的属性可改 ;
final + 实例变量 :new对象的时候,系统不会赋默认值null,必须手动赋值,一旦赋值,不可修改【实际开发中还需添加static修饰】!
static final String COUNTRY : 就是定义常量 COUNTRY,在方法区被加载,而且只有一个 !
总结:final 修饰的变量只能赋值一次!
三、变量、数据类型、运算符
前提知道:字面值(字面量):就是数据、是Java源程序的组成部分
变量 : 内存当中存储数据的最基本的单元
变量三要素 : 数据类型 + 变量名 + 字面值 【类型决定空间大小】【通过名字访问数据】【字面值就是保存的数据】
1.变量的分类
局部变量 : 方法体中声明的变量
成员变量 : 类体内声明的变量
注意:成员变量 = 实例变量(类体中定义的int age) + 静态变量(类体中定义的static int age);
注意:局部变量只在方法体中有效,方法体执行结束,该内存就释放!
2.变量的作用域
作用域:变量的有效作用范围,出了大括号就不认识了!
并且与其他变成语言一样,Java在访问变量的时候存在一个就近原则!
扩展 :int a = 100 ; int b = a ;的赋值原理:将a变量的值复制一份给b变量,所以a、b是两块不同的内存空间
3.数据类型
一、基本数据类型:四大类八小种
- 整数型 : byte(字节型) 【占1Byte空间】、short(短整型)【占2Byte空间】、int(整型)【占4Byte空间】 、long(长整型)【占8Byte空间】
- 浮点型 : float(单精度)【占4Byte空间】、double(双精度)【占8Byte空间】
- 布尔型 : boolean【占1Byte空间】
- 字符型 : char【占2Byte空间】
扩展:
1M = 1024KB 、1KB = 1024Byte 、1Byte = 8 bit 一个bit(比特)相当于一个0或1 ,
一个汉字占用2个字节,可用char来存储 例如 : char c = ‘宋’ ;但不可存’ab’,因为char读取一个字符后会直接找分号、ab是属于字符串!
**二、引用数据类型:**除了基本数据类型外的数据类型
String 和 Class XXX 类型【自定义类型】如Student类
java.math.BigDecimal类型,财务数据类型
4.转义字符
改变原本字符的含义!
/ 正斜杠 \ 反斜杠
\ 是转义字符,会将后面紧挨着的字符转义! 遇到t为 \t 制表符, 遇到n为\n换行符、遇到"则失去其含义成为普通字符(不会再与另一个"配对)!
5.类型转换
规则:等号右边先执行,然后赋值给等号左边的变量
类型转换的话:大容量转小容量,损失精度,如int a = 100L ; 小容量转大容量:没问题,如 long b = 100 ;
int类型的上限是214748647,一般长度最多为9位!
注意 :long = 214748648
,报错因为等号右边不加L默认是整数类型字面值,但是又超出整数范围,所以报错!
强制类型转换:如int a = (int) 100L ; 直接大容量转小容量,损失精度,编译报错,但是进行强转后,虽然编译能过但是仍会损失精度!
强制类型转换的原理:砍去前面的字节!
6.运算符
例如:三目运算符:char c = sex ? ‘男’ :‘女’
当sex == true时, c= ‘男’,当 sex == false时,c=‘女’!;
接收用户键盘输入
为变量赋值!
public class keyInput{
public static void main(string[] args){
java.util.Scanner s = new java.util.Scanner(System.in) ;
int i = s.nextInt() ; //输入整数,此处相当于C++中的 scanf("%d",&i);
String str = s.next() ; //输入字符串
}
}
四、控制语句
1.选择语句
- if 语句
- switch 语句
2.循环语句
- for 循环
- while 循环
- do … . while循环
3.转向语句
-
return : 结束当前循环!
-
continue :结束当次循环,进入下次循环
-
break : 直接跳出循环,只跳出一层循环!
五、方法
先了解方法在JVM的内存结构图
方法区:类加载器将字节码文件加载到JVM当中后,将字节码文件放入方法区,然后找到字节码文件的入口main,然后执行!
注意:只要是方法,无论是构造方法,静态方法,实例方法,都会压(push)栈的!
1.方法重载 Overload
同一个类中,方法名字一致,参数列表(参数的个数、参数的类型、参数的顺序)不一致即为方法重载
2.方法的递归
就是套娃! 写个题测试:递归求n的阶乘、递归求前n个数的和、递归求斐波那契数列、、、
3.方法的覆盖/重写 Override
A.Class exdents B.Class 当子类不满足从父类中继承来的的方法时!A类中重新改写继承过来的方法的方法体内容!
条件:1.同一个方法 方法名字一致,参数列表一致,改写方法体内容,
2. 如果返回值是基本数据类型,重写时,返回值类型必须一致。如果是引用数据类型(Object),则重写可以变小(Cat)一些!,但是意义不大,实际开发并不会这么写!
3.权限不能更低
4.异常不能更多
5.静态方法,不谈覆盖,因为方法覆盖通常跟多态有关,静态方法覆盖也没意义!
6.私有方法不能覆盖
注意:当父类中的方法是protect,我们子类重写方法可以把权限升高为protect、public,但是如果父类中的方法是public,我们重写的时候这个权限是不能降低的,只能是public,而且抛出的异常只能更少不能更多!
4.方法分类
- 实例方法:对象(实例)里的方法,不含static
- 静态方法:类里的方法,含static的方法
- 构造方法:不可被调用,应用于开辟空间
5.方法调用 *
静态方法的调用:通过 “类名 . 方法名 ”,去调用,如果在一个类中,类名可以省略不写(本质就是编译后的字节码文件需要在同一级目录,字节码文件. ) ,【也可以通过引用调静态方法,会JVM会自动把引用转化为Class类,但是不建议使用】
实例方法的调用:通过 ”引用 . 方法名“,去调用,
注意:在除了Main()方法中调用其他实例方法需要new实例之外,其他方法中调用不需要new对象,可以直接方法名调用
六、面向对象
特点:高内聚,低耦合,易扩展
默认情况下,类中的变量分为局部变量和成员变量,以及静态方法
但是,一个类如果被new(实例),那么这个new出来的就是该对象的实例,对象中的变量称作:实例变量,对象中的方法称作:实例方法
创建对象(实例)对应JVM内存图:
1.OOA、OOD、OOP
- OOA(Object Oriented Analysis):面向对象分析
- OOD(Object Oriented Design):面向对象设计
- OOP(Object Oriented Programming):面向对象编程
一个软件的开发过程:A(分析) ---->D(设计)----->P(编程)
2.封装、继承、多态
封装、继承、多态 是面向对象的三大特征
2.1、封装
封装的目的:对外提供简单的入口,不再暴露复杂的数据,保证数据安全
-
采用privite将属性私有化,使得外部不能直接访问!
-
然后提供该属性的,set(),get()方法!,相当于对外提供一个简单的读入口、和写入口,并且可以在set,get方法中设置关卡!
要求严格遵守 set 和 get方法的规范:
//set方法规范
public void setAge(int age){ //方法名规范:get+属性名且首字母大写
this.age = age
}
//get方法规范
public int getAge(){
return this.age;
}
2.2、继承
基本作用:子类继承父类,代码才可以复用 (单继承,一个类只能有一个父类)
主要(重要):因为有了继承关系,才有了后面的,重写和多态!
继承(extends) 的特性:
- 子类可以继承除父类除构造方法外,所有的属性和方法!
- 父类中privite修饰的私有属性,子类继承过来不能直接访问,可以通过间接的访问
- 所有类默认继承Object类(所有类的根!)
- 子类继承父类,父类中的属性和方法归子类所有,所以子类可以直接调用父类的方法!
2.3、多态 【重点】
多态:就是多种形态,编译时一种形态(静态绑定),运行时一种形态(动态绑定)
- 向上转型 :子 ------> 父 (自动类型转换)
- 向下转型 :父 ------> 子 (强制类型转换,需要加强制类型转换符)
无论是向下转型,还是向上转型,两者必须要满足 继承关系
class cat extends Animal { cat独有方法:eat() 、重写animal中方法:move() }
class Bird extends Animal { bird独有方法:fly()、重写animal中方法 move()}
Animal a = new Cat() a.move() //向上转型
Animal a2 = new Cat() (Cat)a2.eat() //向下转型
Animal a3 = new Bird() Cat c = (Cat)a3 c.eat() //报运行时异常java.lang.ClassCastException!即使编译能过!但是运行时a3本质还是一只鸟,Bird与Cat无继承关系,所以运行失败!
因此得出结论向下转型(强转):存在风险!
避免方式: 采用 instance of
instance of 运算符
a3 instance of Cat 在运行时动态判断,引用a3堆内存中指向的是否为一只猫!如果是 return true 否则 return false ;
Animal a3 = new Bird() ;
if(a3 instance of Cat) Cat c = (Cat)a3 ;
c.eat() ;
为什么使用instance of ?
public void test(Animal a){
if(a instance of Cat) Cat c = (Cat) cat
else if(a instance of Bird) Bird b = (Bird) cat
}
//使用instance of 的话,再调用上述方法,使得Cat和Bird都能接收!
3.抽象类
类与类之间的共同特征,这些共同特征抽取出来就是抽象类,但是注意:抽象类无法实例化、是用来继承的!
基础语法:
- 格式:[修饰符列表] abstract class XXX
- 抽象类的子类可以是抽象类,也可以是非抽象类
- final不可修饰抽象类
- 抽象类无法实例化,但是抽象类有构造方法,供创建子类对象使用考虑super!
- 抽象类中不一定含有抽象方法,但是抽象方法只能存在于抽象类中!
- 非抽象类类继承抽象类,必须重写抽象类中的抽象方法 【重要】
3.1、抽象方法
特点:
- 没有方法体,以分号结尾 ;
- 前面修饰符列表存在abstract修饰
4.接口
完全抽象的,是特殊的抽象类!
基础语法:
- 格式:[修饰符列表] interface XXX
- 编译后也是一个字节码文件
- 一个接口,支持多继承,且其中的元素都是公开的public !
- 接口中只能存在常量(static final)+ 抽象方法,
- 接口中抽象方法的前缀(public abstract)可以省略,接口中常量前缀(public static final)也可以省略
- 非抽象类实现接口,必须将接口中的抽象方法全部重写!【重要】
开发中的使用:
一个类支持继承单个父类,实现多个接口!
面向接口编程,解耦合!
5.导包机制
字节码文件不在同一个包下,如果需要调用,需要把需要调用字节码文件的包导入import
package
- 出现在java源文件第一行
- 不在源文件目录编译需要 javac 包名.xxx.java;
- 不在字节码文件目录运行需要 java 包名.xxx
import
-
java.lang包下不需要导入,由编译器javac自动导入
-
import 包名+类名
6.访问控制权限
- private 私有的:private表示私有的修饰的元素,只能在本类中用
- protect 受保护的:protect只能在本类、同包、子类中访问!
- public 公开的:public表示公开的,在任何位置都可以访问!
- 默认的:默认只能在同类或同包下访问
7.API 和 API 帮助文档
API(application program interface ):应用程序编程接口
整个JDK类库就是javaSE的API ,例如:
一般每个API都会配备一个API帮助文档:
8.内部类
在类的内部又定义一个新的类,被称为内部类
- 静态内部类
- 实力内部类
- 局部内部类:匿名内部类
跟记忆变量的分类保持一致!静态内部类和实例内部类在Class内部,局部内部类在方法中,外部不得访问!
8.1、匿名内部类 *
interface compute{
public abstract int sum (int x, int y ); //接口
}
class mymath{
public void mysum(compute c, int x, int y){
int value = c.sum(x,y) ;
}
}
public class Test(){
public static void main(String[] args){
mymath mm = new mymath( );
mm.mysum(new compute(){ //此处就是匿名内部类,原本需要传入一个接口的实现类,
public int sum (int x, int y ) return x+y ; //我们new接口,重写方法就行!,也就是实现类匿名了!
},100 , 200 );
}
}
七、数组
一种引用数据类型,父类是Object,存放数据的容器,存储在堆内存当中
-
数组长度一旦定义,不可改变!
-
数组的内存地址,就是数组内第一个元素的内存地址
-
int a[ ] ,a存储的就是数组a[ ]的首要内存地址,数组的内存地址是连续的!a + 1 = a[1] ;
数组的JVM内存图:
优点 :
- 我们知道数组的第一个元素的内存地址,数组的内存地址又是连续的,我们可以通过偏移量,再通过字节大小推算出后面所有的内存地址精准定位,查询时间复杂度O(1) ;
缺点:
- 为了保证数组的内存地址连续,随机增删数组元素的时候(最后一个元素除外),效率较低,时间复杂度位O(n)!
- 数组不能存储大数据量数据,因为很难找到一块大量的连续的数据空间
语法格式 :
静态初始化:int array[] = {1 , 2 , 3 , 4 , 5 }
动态初始化:int array = new int [5] ; 此处的是指数组长度为5,每个元素默认值为 0 ;
public static void main(String[] args) { } //String[ ] args 字符串数组有什么用?
//JVM加载main方法的时候,会自动传入一个数组过来!
System.out.println(args.length()) //输出0 ,表示长度为0的数组,已创建对象,但是数组里没有东西!
1、数组扩容
扩容机制:创建一个大数组,将小数组的数据一个一个Copy过来!
结论:数组扩容效率较低,因为存在数组拷贝,所以在开发的时候注意:开辟数组的时候空间尽量把握准确!
2、数组拷贝
System类的静态方法arraycopy,会调用底层的C++程序,完成拷贝!
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length); //数组拷贝函数,需要传入5个参数
//源数组、源数组开始下标、目标数组、目标数组开始下标、拷贝的数组长度!
数组长度一旦确定不可改变,若需要增加长度,只能创建一个大数组,然后进行数组拷贝
3、二维数组
特殊的一维数组,一维数组当中的每个元素是一个一维数组 !
静态初始化 : int [][] [ ] [ ] array = {{1,2,3,4} , {1,24,5} , {2,3,4,2,3,4} } //相当于3个一维数组!
length 属性 :
- array.length == 3 ;
- array[0].length == 4 ;
遍历二维数组:
//遍历二维数组
String[][] array = {
{"张三","李四","王五"},
{"Java","C++","C","C#"},
{"RabbitMQ","RocketMQ","kafka","Redis"},
{"Hadoop","Hive","Hbase","Zookeeper"}
};
for(int i = 0 ; i < array.length ; i ++ ){
for (int j = 0; j < array[i].length ; j ++){
System.out.println(array[i][j]);
}
System.out.println("\n");
}
4、数组模拟栈
public class ArrayStack {
public static void main(String[] args) {
int[] stack = new int[1001] ;
int top = 0 ;
//入栈
int x ;
Scanner scanner = new Scanner(System.in);
x = scanner.nextInt() ;
stack[++top] = x ;
//出栈
top -- ;
//判断栈是否为空
if (top > 0) System.out.println("not empty" );
else System.out.println("empty");
//求栈顶元素
int res = stack[top];
}
}
八、String 类
一种引用数据类型,不属于基本数据类型,存储在方法区中的字符串常量池当中!
特点:
- 凡是 “ ” 括起来的都是String类型对象,都在字符串常量池中有一份,且“ ”中的内容是不可变的 !
- 字符串间使用 + 是字符串连接操作符号 “abc” + “def” ===> “abcdef”
- 垃圾回收器是不会释放,字符串常量池中的东西的!
- String类已经重写了equals方法,字符串比较用equals()!
一段代码对应的JVM:
public class StringJVM {
public static void main(String[] args) {
String str = "abc" ;
String str2 = "abc" + "def" ;
String str3 = new String("xy") ;
}
}
//现在堆内存中创建空间,“xy”在常量池开辟空间,这样堆内存保存的就是常量池的地址!
测试:以下代码创建了几个对象 ?
public class StringJVM {
public static void main(String[] args) { //一共创建三个对象,2个在堆内存,一个在方法区的字符串常量池!
String str1 = new String("xy") ;
String str2 = new String("xy") ;
}
}
-----------------------------------------------------------------------------------------------------------
测试内存:
String s1 = "abc" ; //s1、s2 保存的是常量池中的地址
String s2 = "abc" ;
String s3 = new String("abc") ; // s3、s4 保存的是堆内存中的地址
String s4 = new String("abc") ;
if (s1 == s2){
System.out.println("内存地址相等");
}
System.out.println("-----------------------------------");
if (s3 == s4){
System.out.println("内存地址相等");
}else{
System.out.println("内存地址不等");
}
测试结果:
内存地址相等
-----------------------------------
内存地址不等
1、构造方法
String(byte[] bytes)
:传入Byte数组,会通过默认的Ascii码来转化!String(byte[] bytes, int offset, int length)
: 传入Byte数组,offset数组起始下标、length截取的长度String(char[] value)
:传入char数组,将char数组转化为StringString str3 = new String("xy") ;
//初始化字符串
2、String类的常用方法
1、charAt方法
//charAt方法、将通过下标获取字符串中,对象下标的字符!
String name = "宋淇祥" ;
char c = "宋淇祥".charAt(1);
System.out.println(c); //淇
2、compareTo方法
//按照字典序比较字符串
System.out.println("abc".compareTo("abc")); //0 , 表示两个字符串字典序相等
System.out.println("abcd".compareTo("abce")); //-1 ,abcd的字典序小于abce
System.out.println("abce".compareTo("abcd")); // 1 ,abce的字典序大于abcd
3、contains方法
//contains方法
System.out.println("12345678".contains("123")); //true
System.out.println("12".contains("123")); //false
4、endwith方法
//endwith方法 bool类型
System.out.println("123456".endsWith("456")); //123456是否是以456为结尾的?true
5、equals方法【掌握】
重写equals方法,比较的是内容,不重写比较的是内存地址,String已经重写过equals了默认比较字符串内容
== 比较的是内存地址!
//equals方法,重写的Object的方法!
System.out.println("123".equals("123")); //true
//重写后为:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) { //字符串长度一致、长度将字符串转化为字符数组
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i]) //2个字符数组中的元素逐个匹配
return false;
i++;
}
return true;
}
}
return false;
}
6、equalsIgnoreCase方法
判断两个字符串是否相等、忽略大小写
//equalsIgnoreCase
System.out.println("abc".equalsIgnoreCase("ABC")); //true
7、getBytes 方法
将字符串转化为byte数组
//getBytes()
byte[] bytes = "abc".getBytes();
for (byte b : bytes) {
System.out.print(b+" "); //97 98 99
}
8、indexOf方法
//indexOf方法,返回的是123在字符串中第一次出现的索引(下标)
System.out.println("21234561123".indexOf("123")); // 1
9、isEmpty方法
//isEmpty() bool ,判断字符串是否为null,注意:" "并不是空串!
System.out.println("".isEmpty()); //true
//求数组长度,length属性
System.out.println("123".length());
//求字符串长度,length()方法
int[] a = {1,2,3};
System.out.println(a.length);
10、replace方法
字符串替换
//字符串替换
String replace = "dadsadadsada====dasdasda===".replace("=", "+"); //将字符串中的=全部替换为+
System.out.println(replace); //dadsadadsada++++dasdasda+++
11、split方法 【掌握】
字符串拆分
//字符串拆分,返回字符数组
String[] ymd = "qwert=asdffg=zzzcxc".split("=") ; //字符串用=拆分为3个字符串
for (String s : ymd) {
System.out.print(s+ " "); //qwert asdffg zzzcxc
}
12、subString方法 【掌握】
字符串截取
System.out.println("qwert=asdffg=zzzcxc".substring(1, 5)); //wert [开始下标,结束下标),前闭后开
13、toLowerCase方法
字符串全部转换为小写,大写为Upper
System.out.println("ADASDASDASD".toLowerCase()); //adasdasdasd
14、trim方法
去除字符串中的空格
System.out.println(" dsa sda sgg s ".trim());//dsa sda sgg s 去除的仅仅是字符串前后的空格
15、valueOf方法 【掌握】
String中唯一的静态方法,类名.直接调用String.valueOf()
System.out.println(String.valueOf(true)); //将非字符串转化为字符串! "true"
System.out.println(String.valueOf(3.14));
//println方法底层调用String.valueOf方法,只有这样才能调用toString方法!
//能打印在控制台上的都是字符串!
3、StringBuffer
为了解决字符串频繁使用+拼接,创建多个对象,浪费空间!
本质:字符数组 (字符串缓冲区)
StringBuffer extends AbstractStringBuilder
//构造方法!
public StringBuffer() {
super(16); //本质:长度为16的char类型数组,满了之后数组自动扩容为之前的二倍!
}
//父类构造方法!
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
如何对StringBuffer优化?
//初始化StringBuffer时候给一个合适的初始化容量,减少数组拷贝次数
StringBuffer stringBuffer = new StringBuffer(100);
4、StringBuilder
使用方法:与StringBuffer一致!
区别:
- StringBuffer都由synchronized修饰,是线程安全的 !
- StringBuilder无synchronized修饰,非线程安全的 !
九、八种包装类型
为了方便开发而出现的一种机制!
基本数据类型 | 包装数据类型 | 包装类的父类 |
---|---|---|
short | java.lang.Short | java.lang.Number |
byte | java.lang.Byte | java.lang.Number |
int | java.lang.Integer | java.lang.Number |
long | java.lang.Long | java.lang.Number |
boolean | java.lang.Boolean | java.lang.Object |
char | java.lang.Character | java.lang.Object |
float | java.lang.Float | java.lang.Number |
double | java.lang.Double | java.lang.Number |
注意:重点学习java.lang.Integer即可,其余参照!
装箱:将基本数据类型转化为引用数据类型
Integer i = new Integer(100); //装箱
拆箱:将引用数据类型转化为基本数据类型
由于包装类的父类是Number所以会继承Number中的所有方法:
int i2 = i.intValue(); //拆箱
System.out.println(Integer.MAX_VALUE); //获取int的最大取值范围 2147483647
System.out.println(Integer.MIN_VALUE); //获取int的最小取值范围 -2147483648
1、自动装箱自动拆箱
JDK1.5引入了自动了自动装箱,和自动拆箱的概念,以后Number的方法就用不到了!
Integer i = new Integer(100);
int i2 = i.intValue();
//自动化!
int x = i ;
Integer x2 = i2 ;
System.out.println(x2 + 1); //此处遇到+运算符,自动拆箱!
2、Integer的JVM分析
Java中为了提高程序的执行效率,将[-128,127]之间的所有包装对象提前创建好,
放到了一个方法去内存中的一个整型常量池当中,使得这个区间的对象不需要再new了!
Integer a = 127 , Integer b = 127;
System.out.println(a == b); //true ;
Integer a1 = 128 , Integer b1 = 128 ;
System.out.println(a1 == b1); //false ;
注意: == 比较的是内存地址!
Integer重写了equals方法,比较的是内容
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
扩展:当我们Integer类被加载的时候,内部的静态代码块也会执行,创建【-128—127】这256个对象,放入整型常量池的当中
【-128—127】
Integer重点方法:
static int parseInt ( String s ){ }
静态方法:传入String,返回int(但是,只能将整数型字符串转化为int)
static double parseInt ( String s ){ }
静态方法:传入String,返回double(只能将浮点型型字符串转化为double)
多种类型转换:
十、异常
程序执行过程中,不正常的情况 ! 异常机制的作用:增强程序的健壮性
异常是以类的形式存在的,new异常对象 注意:所有的异常都是发生在运行阶段
异常继承结构图:
编译时异常和运行时异常区别:
- 编译时异常:这种异常出现的概率较高,需要再编码阶段进行预处理
- 运行时异常:发生的概率较低,并不需要预处理
1、异常处理
方式一:throw 【异常上抛】
如果上抛到main中,仍然没有处理,就会抛给JVM,然后程序停止!
package com.sqx;
public class ExceptionTest01 {
public static void main(String[] args) {
try {
doSome(); //预处理编译时异常,
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void doSome() throws ClassNotFoundException{ //上抛编译时异常,调用的时候需要预处理
}
}
//编译时异常必须进行预处理!
类似于推卸责任!
方式二:try catch 【异常处理】
结合实际案例理解!
public class ExceptionTest01 {
public static void main(String[] args) {
try {
doSome();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void doSome() throws FileNotFoundException {
FileInputStream inputStream = new FileInputStream(""); //此处如果路径错误会异常向上抛出给main方法处理!
}
}
FileInputStream构造方法!
捕捉就是解决问题!
2、异常常用方法
1、e.printStackTrace(); //打印异常的堆栈信息
2、String str = e. getMessage() ;
3、finally子句
finally子句中的代码是最后且一定会执行的,且需要try catch一起出现!
完整的异常处理流程!
需要注意的是:try和finally可以单独联用,使用步骤如下!
总结:放在finally语句块中的代码是一定会执行的!
4、自定义异常
//自定义编译时异常 , throw抛出该异常时,需要编译预处理!
public class MyException extends Exception {
public MyException() {
}
public MyException(String message) {
super(message); //含有编译时异常的所有特征!
}
}
//自定义运行时异常
public class MyRunException extends RuntimeException{
public MyRunException() {
}
public MyRunException(String message) {
super(message);
}
}
分析:java.util包工具类
1、java.util.Array
-
Array.sort( Object[ ] a ) : 将数组从小到大排序
-
Array.BinarySearch(Object [] a , int x) : 二分法查找 x ,返回的是下标,查不到返回 - 1!
2、java.util.Date
日期工具类
详情如下:
Date date = new Date();
System.out.println(date); //Fri Sep 10 19:05:35 CST 2021
//格式化时间
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String s = dateFormat.format(date); //Date转换为字符串 2021-09-10 21:12:55 533
System.out.println(s);
//将日期字符串转换为Date类型
String time = "2000-07-10 20:59:42" ;
SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date1 = dateFormat1.parse(time); // Mon Jul 10 20:59:42 CST 2000
System.out.println(date1);
//获取从1970年到当前时间的总毫秒数
long timeMillis = System.currentTimeMillis(); //1631279575568
System.out.println(timeMillis);
//构建Date对象传入毫秒数
Date date2 = new Date(1);
SimpleDateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String s1 = dateFormat2.format(date2); // 1970-01-01 08:00:00 001 北京时间、东八区
System.out.println(s1);
扩展:数字格式DecimalFormate
3、java.util.Random
生成随机数!
public class RandomTest {
public static void main(String[] args) {
Random random = new Random();
int num01 = random.nextInt(); //i是一个随机的int类型数据
int num02 = random.nextInt(101); //在[0,101)的区间内产生一个int类型的数据
//例题:生成5个不同的随机数,并且保存在数组当中
int[] arr = new int[5];
for (int i = 0; i < arr.length ; i++) {
arr[i] = -1 ;
}
//循环生成随机数
int index = 0 ;
while(index < arr.length){
int num = random.nextInt() ;
if (!isContains(arr,num)){
arr[index++] = num ;
}
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]+" ");
}
}
public static boolean isContains(int[] arr, int val){ //查询方法!
for (int i = 0 ; i < arr.length ; i++) {
if (arr[i] == val) return true ;
}
return false ;
}
}
分析:java.lang包常用类
1、java.lang.System
简单总结一下System类的相关属性和方法!
System.out ; // out是System的静态变量
System.out.println(); // println()不是System类的,而是printStream类的方法!
System.gc(); //建议启动垃圾回收器
System.currentTimeMillis(); //获取从1970年到当前时间的总毫秒数
2、java.lang.Object
编译器(javac.exe)自动导入java.lang包,原因:因为用得多,提前加载了该包文件,且节省了资源。
Object类下存在如下方法!
-
toString( ) 方法 :
作用:将Java对象转化为字符串的形式
//JDK中toString方法的源代码! public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); //类名 + @ + 将内存地址转化为16进制 }
当我们System.out.println(引用) == System.out.println(引用.toString ( ) ),这个println方法会自动调用这个引用toString方法!
如果不满意输出的对象信息,我们可以重写toString方法,输出想要的对象信息!
-
equals( ) 方法:
作用:比较具体的对象内容,如果内容一直,判断两个对象相等
//默认情况Object的equals方法比较的还是两个对象内存地址,所以我们需要重写equals方法,使得比较的是对象的内容 public boolean equals(Object obj){ return this == obj ; } //注意: "=="比较的是变量的字面值! 重写equals方法比较对象内容是否相同! //String 类型属引用数据类型,存在构造,只能用equals方法,并且String类已经重写了equals方法和toString方法,可以直接用!
结论: Java中基本数据类型使用 ” == “ 判断是否相等
Java中引用数据类型需要使用 equals()方法判断是否相等!
-
finalize()方法:
类似于遗言!,但是此方法于JDK9已经过时!
protected void finalize () throws Throwable { } //这个方法不需要程序员手动调用!JVM的GC机制会自动调用; //对内存中的对象,一般没有被引用,就会被回收! //如果希望对象销毁时机,执行一段代码,这段代码就可以写在finalize方法中! System.gc() //建议JVM启动垃圾回收器,但是不一定启动!
提示:Java中的垃圾回收器(GC)不是随时启动的,垃圾少可能不启动,有很多影响因素!
-
hashcode( ) 方法:
将对象在堆内存的地址转化为等价的一串数字!
public native int hashCode() //不是抽象方法,带有native关键字是会调用底层的C++代码! //就是将java对象在堆内存中的地址,通过hash算法转换的一个数字,同样可以看作一个内存地址!
dateFormat2.format(date2); // 1970-01-01 08:00:00 001 北京时间、东八区
System.out.println(s1);
扩展:数字格式DecimalFormate
### 3、java.util.Random
> 生成随机数!
```java
public class RandomTest {
public static void main(String[] args) {
Random random = new Random();
int num01 = random.nextInt(); //i是一个随机的int类型数据
int num02 = random.nextInt(101); //在[0,101)的区间内产生一个int类型的数据
//例题:生成5个不同的随机数,并且保存在数组当中
int[] arr = new int[5];
for (int i = 0; i < arr.length ; i++) {
arr[i] = -1 ;
}
//循环生成随机数
int index = 0 ;
while(index < arr.length){
int num = random.nextInt() ;
if (!isContains(arr,num)){
arr[index++] = num ;
}
}
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]+" ");
}
}
public static boolean isContains(int[] arr, int val){ //查询方法!
for (int i = 0 ; i < arr.length ; i++) {
if (arr[i] == val) return true ;
}
return false ;
}
}
分析:java.lang包常用类
1、java.lang.System
简单总结一下System类的相关属性和方法!
System.out ; // out是System的静态变量
System.out.println(); // println()不是System类的,而是printStream类的方法!
System.gc(); //建议启动垃圾回收器
System.currentTimeMillis(); //获取从1970年到当前时间的总毫秒数
2、java.lang.Object
编译器(javac.exe)自动导入java.lang包,原因:因为用得多,提前加载了该包文件,且节省了资源。
Object类下存在如下方法!
-
toString( ) 方法 :
作用:将Java对象转化为字符串的形式
//JDK中toString方法的源代码! public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); //类名 + @ + 将内存地址转化为16进制 }
当我们System.out.println(引用) == System.out.println(引用.toString ( ) ),这个println方法会自动调用这个引用toString方法!
如果不满意输出的对象信息,我们可以重写toString方法,输出想要的对象信息!
-
equals( ) 方法:
作用:比较具体的对象内容,如果内容一直,判断两个对象相等
//默认情况Object的equals方法比较的还是两个对象内存地址,所以我们需要重写equals方法,使得比较的是对象的内容 public boolean equals(Object obj){ return this == obj ; } //注意: "=="比较的是变量的字面值! 重写equals方法比较对象内容是否相同! //String 类型属引用数据类型,存在构造,只能用equals方法,并且String类已经重写了equals方法和toString方法,可以直接用!
结论: Java中基本数据类型使用 ” == “ 判断是否相等
Java中引用数据类型需要使用 equals()方法判断是否相等!
-
finalize()方法:
类似于遗言!,但是此方法于JDK9已经过时!
protected void finalize () throws Throwable { } //这个方法不需要程序员手动调用!JVM的GC机制会自动调用; //对内存中的对象,一般没有被引用,就会被回收! //如果希望对象销毁时机,执行一段代码,这段代码就可以写在finalize方法中! System.gc() //建议JVM启动垃圾回收器,但是不一定启动!
提示:Java中的垃圾回收器(GC)不是随时启动的,垃圾少可能不启动,有很多影响因素!
-
hashcode( ) 方法:
将对象在堆内存的地址转化为等价的一串数字!
public native int hashCode() //不是抽象方法,带有native关键字是会调用底层的C++代码! //就是将java对象在堆内存中的地址,通过hash算法转换的一个数字,同样可以看作一个内存地址!