1、数组这个容器本身属于引用数据类型, 数组的父类是Object。
int[] array1 ={100,10,50};(即array1属于引用数据类型,不要以为数组是基本数据类型int)
2、数组是一个容器,同时容纳多个元素,是一个方便程序员储存数据的空间。(数组是一种数据的集合)
1、因为数组是引用数据类型对象,存在堆内存中,因此数组的存的对象元素也是存放在“堆内存”当中。
2、数组自身是引用数据类型,但是数组内部既可以储存“基本数据类型”的数据,也可以储存“引用数据类型(注意存的只是内存地址)”的数据,
3、数组中要求元素类型统一,只能固定存储一种数据类型的数据。不能同时基本+引用类型一起存。
例如;int类型数组只能存int类型,person引用类型数组只能存person类型。
③注意细节:数组内部可以放的类型:!!!!!!!!
1、存储固定的一种数据类型的数据。(可基本可引用)
2、但是数组里面的的对象盒子可以放父类本身的Animal类型,也可以放子类儿子的Cat对象。
3、不能放无继承关系对象。
4、数组中尽量不要存储大数据量的数据,存大数据有集合存储机制(后面一章讲)
5、数组中储存的每个元素都其内存地址和下标也是存放在“堆内存”当中,具有连续性和空间一致性)
6、数组自身的对象内存地址就是第一个元素的内存地址.(注意数组的对象引用,即数组对象引用的内存地址是在“栈内存”当中的,只是指向堆内存中数组对象地址和存储的元素对象各个地址)
7、数组一旦创建出来,数组的length长度不可变,长度决定元素个数。
数组自身引用对象都自带length属性(java自带的),用来获取数组中的元素个数。 System.out.println(a.length);
8、数组中有三个内存地址需要详解一下?
①数组中每一个装数据的盒子都是有其对应的“内存地址”。
0x55是数组自身是引用类型的地址(等于第一个盒子的内存地址)
②0x56、0x57、0x58连续是数组里面的每一个盒子内存地址。
③0x1234是放进去数组的引用对象(地址)。(注意不能直接存java对象,存对象的地址,对象内存地址然后再去指向具体对象)
总结:说白了就是数组本身自己有一个引用(栈)地址,他的内部每一个元素盒子也有对应(堆)地址,然后存的元素如果是引用类型也是存的引用地址。三个地址。
下面图片展示:
9、【1】数组优点(检索效率高)?(不会一个一个找,是通过公式先算出该元素的内存地址,然后再定位到元素)
①因为数组内部盒子的内存地址是“连续性“的,Java的数组中存储的每个元素在空间存储顺序上是连续的,
②Java的数组中存储的每个元素类型一致,也就是说(数组内部每个元素“占用的空间大小一致”,空间一致性)
③数组每个元素都有“下标”,(数组有下标后就可以计算定位出被查找的元素和首元素的偏移量)。
④数组的首元素的内存地址是作为整个数组对象的内存地址,(因此我们是提前知道首元素内存地址的)
综合上面四点:
当通过直到第一个元素地址(即数组自身地址),同时因为地址储存的连续性和知道每一个元素类型的所占内存空间后,就可以在数学计算之后,去得到某个下标上面所对应的元素内存地址,最后通过内存地址来定位该被查找元素,因此检索效率高。
【2】数组的缺点(增删的效率较低)?
但正因为要保证内存地址连续,所以删除或者增加内部某个元素都会发生较大的变动位移(要往前挪或者往后移),
因此增删的效率较低。但是删除和增加第一个或者最后一个元素没影响
(因此底层为数组结构的集合中利用头插法或尾插法插入数组的增删效率最高,因为没影响啊)。
10、扩展知识:
java数组写法:int[] a1 ={123,200,100,10,50,189};
c++数组写法;int a1[] ={123,200,100,10,50,189};
11、数组分类?
一维数组,二维数组,三维数组,四维数组(一、二维数组最常用。)
1、一维数组的元素访问?
语法结构:数组名[下标];
数组中每一个盒子都是有对应的“下标”的。
A、下标作用?(可以通过下标来访问读取和修改元素,因此检索效率高)
读取第一个元素:a1[0] 、 改第一个元素:a1[0]= 100;
B、下标从0开始,以length-1结尾。
C、下标规律:例如长度是6,则下标要减一即【0-5】,
2、如何初始化一个一维数组容量?
(有动静态初始化两种,作用都是:给数组初始化容量)
【1】静态初始化语法:类型[] 数组名={元素,元素};
int[] array1 ={ 100,10,50};
Animal[] animals3 ={new Cat(),new Bird()}; /Animal[] animals = {a1,a2};
(注意点:静态初始化就是中括号放“实参123”或者"实参new Animal()",如果是方法引用名要先new元素对象)
Animal a1 = new Animal(); / Animal a2 = new Animal();
【2】动态初始化语法:类型[] 数组名=new 类型[元素的个数];
String[] string1 = new String[3];
Animal[] animals2 = new Animal[2];
(动态初始化就是new数组放个数,不用提前new对象,但是后面要new对象)
animals2[0] = new Animal();/ animals2[1] = new Cat();
***四、一维数组的动静态初始化和如何遍历数组代码:***
```java
/*
1、什么时候用静态初始化,什么时候用动态初始化?
当你创建数组时候,确定数组要存储那些数据,(基本类型或String)采用静态初始化 静态初始化输出(提前放实参)=具体实参数值。
当你创建数组时候,不确定数组要存储那些数据,(多态引用类型)采用动态初始化,预先分配空间, 动态初始化输出= 默认值null, 预先分配空间,可以后期自己手动赋值 object1[0] = xxx; a2[0]= xxx; string1[x] = xxx;
2、具体动静态初始化;
基本数据类型数组,静态初始化输出(提前放实参)=具体实参数值123,因此无默认值了。
动态初始化输出= 默认值0/false/0.0
引用数据类型数组,静态初始化输出(提前new对象放对象o1)= 存的是对象内存地址。 优点;必须提前new好了对象,直接通过下标访问方法
动态初始化输出= 对象类实例属性默认值null 优点;可以后new对象
2、引用数据类型(接口,类,抽象类,数组)
都可以直接或者间接new对象(多态),其对象类属性默认值为null。
也可以实例属性化(多态)。
*/
package 一维数组;
public class 静动态初始化一维数组 {
public static void main(String[] args) {
//定义一个int类型数组,采用动态初始化的方法遍历数组 (具体数值推荐静态初始化)
int[] a2 = new int[4]; // 动态int[4];的[4] 等于a2.length 求元素个数。
System.out.println(a2.length);
for (int i = 0; i < a2.length; i++) {
System.out.println("数组中下标为" + i + "的元素为" + a2[i]); //当i= 0时,输出数组中下标为0的元素为a2[0]
//即是,数组中下标为0的元素为0,因为int类型默认值为0
}
//定义一个int类型数组,采用静态初始化的方法
int[] a1 ={
123,200,100,10,50,189};
for (int i = 0; i< a1.length;i++) {
System.out.println("数组中下标为" + i + "的元素为" + a1[i]); //当i= 0时,输出数组中下标为0的元素为a2[0],即第一个元素123
//即是,数组中下标为0的元素为123
}
//定义一个Object类型数组,采用动态初始化的方法遍历数组 (推荐动)
Object[] object1 = new Object[4];
for (int i = 0; i < object1.length; i++) {
System.out.println("数组中下标为" + i + "的元素为" + object1[i]); //当i= 0时,输出数组中下标为0的集第一个元素为a2[0]
//数组中下标为0的元素为null,因为Object引用类型默认值为null
object1[0].toString();
}
//定义一个Object类型数组,采用静态初始化的方法遍历数组。
Object o1 = new Object();
Object o2 = new Object();
Object o3 = new Object();
Object[] object2 = {
o1,o2,o3};//或者Object[] object2 = {new Object(),new Object(),new Object()}; 可以直接放new实参。
for (int i = 0; i < object2.length; i++){
/*Object o = object2[i];
System.out.println(o);*/ //java.lang.Object@1540e19d
System.out.println("数组中下标为" + i + "的元素为" + object2[i].toString());
//这里注意了,引用类型object2[0]赋值是第一个实例引用对象01,因此输出的是一个对象地址java.lang.Object@1540e19d。
//数组中下标为0的元素为java.lang.Object@1540e19d
}
System.out.println("========================================");
//定义一个String字符串和对象类类型数组,采用动态初始化的方法遍历数组
String[] string1 = new String[3];
for (int x = 0; x < string1.length; x++) {
System.out.println("数组中下标为" + x + "的元素为" + string1[x]);
//输出;数组中下标为0的元素为null, 因为Sting引用类型默认值为null
}
//定义一个String字面值类型数组,采用静态初始化的方法遍历数组
String[] string2 = {
"abc","xyz","lry"};
for (int x = 0; x < string1.length; x++){
System.out.println("数组中下标为" + x + "的元素为" + string2[x]);
}
//定义一个String引用对象类型数组,采用静态初始化的方法遍历数组
String s1 = new String("123"); //sun公司已经写好了String的构造方法和重写了父类Object全部方法。
String s2 = new String("456");
String s3 = new String("789");
String[] string4 = {
s1,s2,s3};
for (int x = 0; x < string4.length; x++){
System.out.println("数组中下标为" + x + "的元素为" + string4[x]); //sun已经重写好了toString方法,引用也可以直接输出数值
}
}
}
1、二维数组的概念?
一个数组中的元素都是一维数组,这个数组被称为二维数组。
2、二维数组的初始化?
①静态初始化语法:类型[][] 数组名={ {元素,元素},{元素,元素}};
②动态初始化语法:类型[][] 数组名=new 类型[一维数组的个数] 【每个一维数组元素的个数】;
例如new int[3][4];代表3个一维数组,每个一维数组4个元素
3、二维数组的元素访问?
语法结构;
数组名+[下标1]+[下标2] 不等于 动态 new 类型[一维数组的个数][一维数组元素的个数]; 不要弄混
下标1;代表二维数组中的“一维数组下标” x行轴 s1[i]
下标2;代表一维数组的“具体元素的下标” y列轴 s1[i][j]
矩阵法;把二维数组看成一个xy坐标轴定位具体元素,
例如,。s1[i][j]理解为,即二维数组s1的第i个一维数组元素中的第j个元素
注意;下标是从0开始的。
4、注意点;String[][] s1
s1.length 代表二维数组自身的长度,也代表二维里面含有多少个一维数组
s1[i].length代表二维数组中的第i+1个的一维数组的长度(一维里面有多少个具体元素)
package 二维数组;
public class 二维数组概念 {
public static void main(String[] args) {
//一维数组
int[] array = {
123,456,789,123456};
System.out.println(array.length);//4 一维数组自身的长度
System.out.println(array[0]);// 123 一维数组里面的第一个元素的值
//二维数组
int[][] array1 = {
{
11,2,3,9},
{
2,3},
{
4,5,6}};
System.out.println(array1.length); //3 二维数组自身的长度,也代表二维里面含有多少个一维数组
System.out.println(array1[0].length);//4 二维数组里面的第一个一维数组的长度。套娃
System.out.println(array1[0][0]);//11 二维数组中的第一个数组中的第一个元素。
}
}
1、动态初始化可以给任何类型元素默认值,而它的元素的默认值值取决于是什么类型的数组,因此默认值可能为null/0/0.0,但都一定是动态初始化容量先!!!
①动态初始化:
Object[] obj1 = new Object[1];
System.out.println(obj1[0]); // null
String[] name = new String[6];//先动态初始化容量,之后赋予6个元素值都为String类型默认值为0。
System.out.println(name[0]); // null
int[] a2 = new int[4];
System.out.println(a2[0]); // 0
先动态初始化容量,之后赋予4个元素值都为int类型默认值为0。
②静态初始化,无默认值了
Object[] obj = {
new Object(),new Object()};
System.out.println(obj[0]); //无默认值,只会输出对象地址java.lang.Object@1540e19d
int[] a1 ={
123,200,100,10,50,189}; //里面已经存有具体元素了,无默认值。
2、数组如果是作为“实例属性”在类中充当实例变量的话,需要在无参或者有参构造先“动态初始化容量”才能赋予内部元素对象默认值。!!!
(并且动态初始化的时候不能重复声明类型,否则就当成是新的属性)
class MyStack{
private Object[] elements; //数组实例属性
private int index; //普通实例属性
//无参构造方法
public MyStack() {
//通过无参构造自己给参数赋默认值
this.elements = new Object[10];// 必须先动态初始化容量
//不能写 Object[] elements = new Object[10];
this.index = 0;//默认值为0。
}
3、当数组的初始化容量和new元素对象一起时,则声明也不能重复。
Animal[] animals2 = new Animal[2];// 初始化容量(说明里面可以储存两个Animal类型对象.()
//因为上面已经提前定义了Animal类型,所以同一作用域中,声明不能重复,直接用引用animals2[0]来new对象或调方法即可)
animals2[0] = new Animal(); //注意; animals2[0]是引用名。
Animals aa = animals[0];
aa.move();
4、如何遍历一个,多态数组(共存子父类对象)+ 如何数组如何向上向下转型【和多态联合】
①前提弄清楚:
A、 animals2是数组的自身引用名,只能用来new数组初始化容量 和 声明了数组储存类型(因而后面的 animals2[i] 不能重复声明类型了)。
B、 animals2[i]是数组储存的其中一个对象的引用名,用来new具体元素的对象。(可多态)
② 总结 一下animals2[i]的作用:作为引用名:
①可以作为引用名直接new具体元素对象(已声明类型):例如,animals2[0] = new Animal(); / animals2[0] = new Cat(多态);
③可以复制给同类型的新引用:Animals aa = animals[i] (这一步经常要用,为了美观好看代码特别是多态的时候,思路会清晰很多)
因为animals[i]引用已定义为Animal类型,因此可以复制引用给新的变量aa,即Animals aa = animals[i] 去指向同一个堆内存地址。(aa引用名好看一下)
④可以作为具体对象引用去调自身类方法;animals[i].move(); / aa.move();
⑤可以作为具体对象引用多态向上转型: animals2[i] = new Cat(); ---》animals[i].move(); 再去多态调子类的覆盖重写方法。
④可以作为引用向下亲子鉴定: if(animals2[i] instanceof Cats) {
animals2[i].catchMouse();}
父类引用如果之前指向的是一个Cat的话那么就可以强转为Cat,然后多态调子类cat特有方法。
package 一维数组;
public class 一维数组和多态联合 {
public static void main(String[] args) {
//静态初始化,创建一个Animal数组,数组当中同时储存Cat和Bird类型。(提前new子类对象,存谁new谁)。
Cats c = new Cats();
Birds b = new Birds();
Animals[] ans ={
c,b};// 静态初始化引用类型的数组
for (int i =0; i < ans.length;i++ ){
//遍历多态的Animal数组。
// ans[i]引用名 ,这个Animal类型数组取出来有可能是个new Cats()对象,也有可能是个new Birds()对象。但ans[i]自身一定是一个Animal类型。
// // Cats c2 = ans[0];
// 比如这里就会直接报错,ans[0]一定只能是一个Animals类型,而不是Cat类型,
// 只是能让你暂时放子类对象,然后父类多态指向子类对象而已,因此类型不兼容,因此需要向下强转。
//修改:
ans[0]= new Cats(); //先多态向上
if(ans[i] instanceof Cats ){
//中途亲子鉴定
Cats cc = (Cats)ans[i]; //再多态向下转,访问子类特有的方法 当Animals类型ans[i]的对象为Cats时,可以强转为Cats
cc.catchMouse();
}else if (ans[i] instanceof Birds){
Birds bb = (Birds)ans[i]; //当Animals类型ans[i]的对象为Birds时,可以强转为Birds,再访问其特有方法
bb.catchSection();
}
}
//引用对象浅拷贝:
Animals aa = ans[0];
// 相反这里不会报错,ans[i]定义是个Animals类型,可以直接复制ans[i]引用给 Animals 的 aa引用。
//两个引用会同时指向同一个堆内存地址
ans[0].move(); //静态绑定会自动去编译父类已有的方法,等动态绑定会去调用猫和鸟重写的方法
aa.move();
}
}
class Animals{
public void move(){
System.out.println("动物在移动");
}
}
class Products{
public void move(){
System.out.println("动物在移动");
}
}
class Cats extends Animals{
@Override
public void move() {
System.out.println("猫在走猫步");
} //父类共有方法,向上转型
public void catchMouse(){
System.out.println("猫在抓老鼠"); //子类特有方法,向下转型
}
}
class Birds extends Animals{
public void move() {
System.out.println("鸟儿在飞翔");
}
public void catchSection() {
System.out.println("鸟儿在抓昆虫");
}
}
六、关于数组的拷贝和扩容:
(一维数组的拷贝 == 间接扩容)
1、java开发中数组长度一旦确定不可变。Animal[] a = new Animal[3]; 长度一旦确定为3个只能放三个元素,即下标为0-2。
2、那么数组满了怎么办?
需要扩容,新建一个更大容量的数组,将小容量数组一个一个拷贝进去即可。
3、开发中尽量少用数组扩容,因为要拷贝效率慢,要自己提前预估长度(个数)多少合适。
4、拷贝工具方法(静态方法): System.arraycopy();
sun公司已经重写好的了,直接调用;
src源代码;public static void arraycopy(Object src,int srcPos,Object dest,int destPos,int length);5个参数
Object src;源头的数组名 (被拷贝的数组名)
int srcPos;被拷贝元素的起始位置下标 (从哪里开始拷贝)
Object dest; 目标的数组名 (要拷贝的数组名)
int destPos; 目标元素的起始位置下标 (拷贝去哪里)
int length; 要拷贝的长度 (拷贝多少个)
package 一维数组;
public class 数组扩容和拷贝 {
public static void main(String[] args) {
//java怎么进行数组拷贝?
// 用拷贝方法,直接调用sun公司已经写好的
// System.arraycopy();//5个参数
//第一种,拷贝基本数据类型
// 拷贝源头(从这个数组中拷贝具体)
int[] src = {
1,11,22,3,4}; //静态初始化
//拷贝目标(拷贝到这个数组上)
int[] dest = new int[10]; //动态初始化
System.arraycopy(src,1,dest,3,2); //代表从数组下标为1的元素,拷贝去数组下标为3的元素,拷贝2个长度即可。
for (int i=0; i< dest.length;i++ ){
System.out.println(dest[i]); //0 0 0 11 22 ...0
}
//第二种,拷贝引用类型。
Animal a1 = new Animal();
Animal a2 = new Animal();
Animal[] src02 = {
a1,a2};//存的是对象地址,不是具体的a1,a2
Animal[] dest02 = new Animal[10];
System.arraycopy(src02,0,dest02,0,src02.length); //拷贝的也是地址。
for (int i=0; i< dest02.length;i++ ){
System.out.println(dest02[i]); //Animal@1540e19d Animal@677327b6 ...null
}
}
}
七、解释main方法String数组 :
1、方法的实参和形参可以是一维和二维数组
2、String[] args= 是代表一个数组,args其实就是一个数组名
1、①String[] args= 是代表一个数组,args是一个数组自身名;
②arg[0]和arg[i] args[i] ,是代表其中的一个元素的引用名:
①String[] args =Null,即表示数组本身引用对象为空,则才会出现空指针异常。
③String[] args= new String[x];x代表的是数组里面可以存放x个对象,不代表下标,不要和arg[x]弄混。
②String[] args= new String[0]:和String [] args={
};
虽然是0和空括号,但一样是有数组args自身对象的,JVM已经分配了一个空间,不会为args=null。只是里面没数据,长度为零。 null null o 0.0
而真正可以为null值的是里面的String属性默认值或者里面对象为null还没new出来的空地址,动态初始化的每个元素会给予默认值为null/0/0.0,看是什么类型的数组;引用、String、int、double
②输出arg[0],即表示输出第1个引用元素的值,这时会有对应的静态具体值1/2/3 或者 动态默认值Null/0/0.0/false。
③动态初始化String[] args = new String[0],这里的 new String[0] 或者
静态初始化String [] args={
},这里的空括号
以上两种初始化都会,让arg.length 长度输出0。
说明数组里面的长度也就是元素个数为0,说明里面没有数据而已,要用户自己上传。
2、主方法的String[] args数组是用来给用户输入参数,配置参数的。
在idea,run运行处,点击配置参数,进入里面提前配置好String数组要对应实际参数
package main方法String数组;
public class 解释main方法String数组 {
public static void main(String[] args) {
System.out.println(args); //是一个哈希地址[Ljava.lang.String;@1540e19d,好消息,说明数组自身对象args不是null,不会出现空指针异常。
// 说明是有这个数组对象的,JVM已经分配了一个空间,
System.out.println(args.length); //0 输出0,这说明JVM调main方法时,JVM那边默认传过来的数组对象长度为0,即里面没有元素。
for (int i = 0;i<args.length;i++){
//程序运行到这里就会结束,因为args.length= 0,已经不满足了 。下面无法输出。
System.out.println(args[i]); //解决办法,去idea,运行处,点击配置参数,进入里面提前配置好String数组的实际参数
//即String[] args = {传xxx,传xxx,传xxx}
}
//区分String[] s1 = new String[0] VS String[] s2 = null !!!!
String[] s1 = new String[0]; //数组自身是有对象的,说明已经分配了一个空间,只是内容元素为0个。
/*String[] s2 = null; //数组自身为null,回收了空间,就会Exception in thread "main" java.lang.NullPointerException
dosome(s1);
dosome(s2);*/
}
public static void dosome(String[]arry){
System.out.println(arry.length); //0 和main方法同理,说明了传过来的数组里面长度为0,即没有参数,要自己输入。
}
}
解释main方法数组的案例:
package main方法String数组;
public class 解释main方法数组的案例 {
public static void main(String[] args) {
//模拟一个用户登陆系统,必须输出用户名和密码,不然报错。
//用户名和密码必须输入到String[]args数组当中。在idea的运行点击参数配置里面传
if (args.length != 2 ){
System.out.println("使用该系统时请输入系统参数,参数中必须包括用户名和密码信息");
return;
}
//程序执行到此处,说明用户输入了自己的用户名和密码
//接下来要判断用户名和密码是否正确
//先取出用户名和密码
String username = args[0]; //取出String[] args数组的用户名 张三
String password = args[1]; //取出String[] args数组的密码 123456
//假设用户名是张三 密码是123456,表示登陆成功,其他一律失败
//判断两个字符串是否相等,需要equals方法 String类的sun已经重写好了equals方法,可以直接用。
// if (username.equals("张三")&& password.equals("123456")){}//这样写一旦username或者password为null可能会出现空指针异常
if ("张三".equals(username)&& "1234560".equals(password)){
//老程序员都这样反过来写,这样写不会出现空指针,因为张三和123456这两个String类型已经是具体值了。
System.out.println("登陆成功,欢迎尊贵的"+username+"会员回来");
System.out.println("您可以继续使用使用该系统");
}else{
System.out.println("对不起,您输出的账户和密码不正确");
}
}
}
八、遍历一维二维数组的总方式:有for 和 foreach 两种
1、普通for循环遍历数组;
for
2、foreach循环遍历数组;(更加简便)
什么是foreach循环,这是一种更加便利的循环工具,循环出数组或者集合中的每一个元素
foreach语法:(切记:遍历的变量名是自己定义的。)
①一维数组;
for(元素的类型 遍历的变量名:数组名或者集合名){
System.out.println(遍历的变量名);
}
②二维数组;
for(元素的类型[] 遍历的变量名:数组名或者集合名){
for(元素的类型 遍历的变量名:数组名或者集合名){
System.out.println(遍历的变量名);
}
}
public class 遍历数组 {
public static void main(String[] args) {
int[] i={
7,4,8}; //一维数组
int[] i1=new int[3];
int[][] i2={
i,i1};//二维数组
int[][] i3=new int[2][3]; //两个一维数组,,每个一维数组3个0,一共6个0
//print方法都是for循环遍历
print(i);// 7 4 8
System.out.println(); //下面全部 System.out.print一行遍历输出,全部输出完了,再一次性换行
print(i1);// 0 0 0
System.out.println();
print(i2);// 7 4 8, 0 0 0
System.out.println();
print(i3);// 0 0 0 0 0 0
Test.foreach();//foreach循环遍历数组
}
public static void print(int[] i){
// for循环遍历 一维数组
for(int c=0;c<i.length;c++){
System.out.print(" "+i[c]); //全部一行输出不换行
}
}
public static void print(int[][] i){
//for循环遍历 二维数组
for(int c=0;c<i.length;c++){
for(int c1=0;c1<i[c].length;c1++){
System.out.print(" "+i[c][c1]);
}
System.out.println();
}
}
}
class Test {
//foreach循环遍历数组
public static void foreach() {
int[] i={
7,4,8};
int[] i1=new int[3];
int[][] i2={
i,i1};
int[][] i3=new int[2][3];
for(元素的类型 遍历的变量名:数组名或者集合名){}
for(int i4:i){
//一维数组foreach遍历
System.out.print(" "+i4);// 7 4 8 这里也是先不换行,一行输出
}
System.out.println(); //全部输出完了,再一次性换行
for(int[] i5:i2){
//二维数组i2foreach遍历
for(int i6:i5){
//foreach再次遍历一维数组i5
System.out.print(" "+i6);// 7 4 8 0 0 0
}
System.out.println();
}
}
}
九、数组开发实际案例:
数组模拟栈数据结构高级案例:
/*
编写一个程序:(使用一维数组,模拟栈内存数据结构)即用数组充当栈内存。
假设该数组的默认初始化容量为10,注意无参数构造方法的写法)
1、这个栈内存可以存任何的引用数据类型。//即用Object[]数组(万能口袋),是所有引用类型的父类,因此里面可以存所有的引用类型;
2、在栈中使用push方法,模拟压栈(栈满了,要有压栈信息)
3、在栈中使用pop方法,模拟弹栈(栈空了,要有弹栈信息)
4、编写测试程序,new栈对象,然后模拟压栈和弹栈的动作。
补充:栈帧是指,栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区块,是一个数据集,是一个有关方法 (Method) 和运行期数据的数据集。(相当于一个记录过程)
栈帧在数组里面就是指,每一个数组空盒子。
*/
package com.bjpowernode.数组进阶案例.数组模拟栈数据结构;
public class 数组模拟栈数据结构案例 {
public static void main(String[] args) {
//创建一个栈对象,动态初始化容量为10
MyStack stack = new MyStack(); //这里是通过无参构造给数组赋值,和设置容量长度。也可以直接有参构造,自己赋默认值MyStack stack = new MyStack(new Object[10]);
//调用压栈方法。
stack.push(new Object()); //因为引用类型,实际保存的都是内存地址,因此下面这三个都是输出对象的内存地址,因为输出引用,实际是去调toString方法,没重写toString方法只能直接输出地址。
stack.push(new Husband());//传过去实现多态Object obj = new Husband();
stack.push(new Wife());
stack.push("字符串类型对象");
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());//栈帧为为第9时,已经满了,无法继续压栈。
stack.push(new Object());//压栈失败
stack.push(new Object());//同上
//开始弹栈
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();//此时栈帧指向-1时,栈已经空了,无法再继续往外弹出。
stack.pop();//弹栈失败了。
}
}
class MyStack{
private Object[] elements; //数组属性
private int index; //栈帧属性,即数组空盒子的下标=elements.length
//如果index等于0,代表栈帧指向栈顶部元素第一个元素.
//如果index等于-1,代表栈帧指向栈顶部元素的还要再上一个位置。
//因此如果index=elements.length-1(9)就说明栈帧满了。
//无参构造方法
public MyStack() {
//通过无参构造自己给参数赋默认值
this.elements = new Object[10];//
this.index = -1;//下标即每一块栈帧
}
//压栈方法,当一个方法A被调用时就产生了一个栈帧 F1,并被压入到栈中,
public void push(Object obj){
if (this.index >= elements.length-1){
//如果index=elements.length-1(9)就说明栈帧满了。
System.out.println("栈已经满了,压栈失败");
return;//直接而暴力中断当前方法;
}else{
//能执行到这说明栈没满,就继续下一个数组盒子去压栈
this.index++;//因此this.index = -1;是最好的,因为判断完然后自加++完下标为0,即数组第一个盒子。
this.elements[index]=obj; //接着把obj引用数据放进下一个盒子当中。即把当前obj数据压入栈中。(压栈)
System.out.println("压栈"+ obj.toString() +"成功,栈帧指向:" + index);//压完栈后,输出压栈状态。指向那个栈帧(盒子)
}
}
//弹栈方法。遵循“先进后出”/“后进先出”原则
/**弹栈方法。遵循“先进后出”/“后进先出”原则
*弹栈方法,从数组中往外取元素。每取出一个元素,栈帧往回移动一位即下标往回移动一位。直到栈空了,直到栈为-1时。(倒序弹栈,正序压栈)
* @return
*/
public void pop(){
if (index < 0){
//执行到此处,此时index下标已经为9了,
System.out.println("弹栈失败,栈已空");
return;
//程序执行到这里,说明栈还没空,可以继续弹。每取出一个元素,栈帧往回移动一位即下标往回移动一位。直到栈空了,即直到栈帧指向-1时。(倒序弹栈,正序压栈)
}else{
System.out.println("弹栈"+ elements[index]+"元素成功。");//此时的下标index已经为9,可以直接弹栈,先把对应的盒子元素弹出,再去往回换上一个下标继续弹上一个下标元素
index--;//(倒序弹栈,正序压栈)
System.out.println("栈帧指向"+index); //为什么要分开,因为弹出之后,当前的栈帧已经自动释放了,因此栈帧要往回指向的是上一个栈帧。
}
}
//有参构造方法
public MyStack(Object[] elements) {
this.elements = elements;
}
//getset封装该数组,不管用不用得上,要保持一个良好的习惯。
public Object[] getElements() {
return elements;
}
public void setElements(Object[] elements) {
this.elements = elements;
}
}
//编写一个类测试 Object[] elements怎么个万能法?
class Objects{
public static void dosome(){
Object[] o = {
new Husband(),new Wife(),"String字符串"};
}
}
//编写一个丈夫类
class Husband{
}
//编写一个妻子类
class Wife{
}
酒店管理系统高级综合数组案例:
/*
①创建好房间类,写好所有私有信息,重写好所有方法,
②创建酒店类,写好构造方法。
①通过酒店Hotel无参构造方法,new房间数组对象,来给私有属性房间Room的有参构造建方法赋值所有的房间信息,因此数组也要在酒店无参里面使用
⑤主方法直接new酒店对象去访问打印方法,即可遍历所有二维数组,即是所有房间的信息
*/
package com.bjpowernode.数组进阶案例.综合酒店案例;
import java.util.Scanner;
public class 酒店管理系统综合案例 {
public static void main(String[] args) {
/* Hotel hotel = new Hotel();
hotel.print(); //调用酒店的打印方法,里面是遍历二维数组的所有房间信息。*/
//下面使用键盘输入来进入管理酒店系统
// Room[][] room = new Room[3][10];
Hotel hotel = new Hotel();
while (true){
//嵌套一个死循环while循环,一直为true不会让下面系统执行一次就结束,可以多次查看和使用功能。
System.out.println("欢迎使用酒店管理系统,请你认真阅读以下说明");
System.out.println("请输入对应的功能编号;[1]表示查看房间列表,[2]表示订房,[3]表示退房,[0]表示退出");
Scanner scanner = new Scanner(System.in);
System.out.println("请输入功能编号");
int i = scanner.nextInt();//这个int只是用来出入功能编号,用来进入系统
if (i==1){
//功能;查看房间状态
hotel.print();
}else if (i ==2){
//功能;订房
System.out.println("请输入房间编号");
int roomNo = scanner.nextInt(); //前台输出房间编号
if (roomNo>=101 && roomNo<=310){
//加个if语句,来判断房间号是否超出范围。
hotel.order(roomNo);//调取方法订房
}else {
System.out.println("输入的房间号有误,请重新输入");
}
}else if (i==3){
//功能;退房
System.out.println("请输入房间编号");
int roomNo = scanner.nextInt();//前台出入房间编号
if (roomNo>=101 && roomNo<=310){
//加个if语句,来判断房间号是否超出范围。
hotel.exit(roomNo);//调取方法退房
}else {
System.out.println("输入的房间号有误,请重新输入");
}
}else if (i==0){
//功能;退出系统
System.out.println("再见,欢迎下次再来!");
}else {
System.out.println("对不起,您出入的功能编号有误,请重新输入!");
}
}
}
}
package com.bjpowernode.数组进阶案例.综合酒店案例;
public class Hotel {
//酒店①有名字 ②房间对象及其属性 ③房间状态方法,订退房间的方法。
private String name;//酒店属性名字
private Room[][] rooms; // 房间数组属性;
public Hotel(){
//这里是通过酒店无参构造方法,来给房间的有参构造建方法建酒店外壳和里面所有的房间信息,因此数组也要在里面使用
//动态初始化房间二维数组;不是new对象
rooms = new Room[3][10]; //建酒店,三层楼每层楼10间房子。 //注意这里不能写 Room rooms = new Room[3][10]; 声明不能重复,不然这里会空指针,声明重复就是新建的一个属性了,没有赋值就会空指针
//下面用构造方法和数组建酒店,全部每层每个房间都建好。
for(int i = 0;i < rooms.length;i++){
for (int j = 0;j< rooms[i].length;j++){
if(i == 0){
//new一楼房间对象并且构造赋值。
rooms[i][j] = new Room((i+1)*100+j+1,"单人间",true); //直接给二维数组的一维数组的每一个房间盒子,new 一个对象即可,不用再次声明类型。
//(i+1)*100+j+1;每层的房间号 i和j下标为0时,则表示第一个房间101
}else if (i ==1){
//new二楼房间对象并且构造赋值。
rooms[i][j] = new Room((i+1)*100+j+1,"标准间",true);
}else if (i ==2){
//new三楼房间对象并且构造赋值。
rooms[i][j] = new Room((i+1)*100+j+1,"总统套房",true);
}
}
}
}
//第一方法、在酒店对象里面,定义一个打印方法,打印所有房间的信息状态,就是遍历二维数组。
public void print(){
for(int i = 0; i < rooms.length;i++){
for (int j = 0; j< rooms[i].length;j++){
Room room = rooms[i][j];
System.out.print(room); //打印房间信息
System.out.println(); //打印一个房间就换一行,就是一个房间信息一行
}
System.out.println(); System.out.println();//打印完一层楼也整体换一行,一层一层看
}
}
//第二方法,订房方法
public void order(int 房间号){
Room room = rooms[房间号/100-1][房间号%100+1];//这个公式要自己想算法,通过房间号来演算出下标.
room.setStatus(false); //输入房间号,Room属性封装之后只能通过set方法修改房间状态,调用就能订房。 不能用get,get是用来访问原来的赋值。不是修改
System.out.println(房间号+"已经订房");
}
//第二方法,退房方法
public void exit(int 房间号){
Room room = rooms[房间号/100-1][房间号%100+1];
room.setStatus(true); //输入房间号,Room属性封装之后只能通过通过set方法修改房间状态,调用就可以退房。
System.out.println(房间号+"已经退房");
}
public Hotel(String name, Room[][] rooms) {
//有参构造酒店
this.name = name;
this.rooms = rooms;
}
}
package com.bjpowernode.数组进阶案例.综合酒店案例;
import java.util.Objects;
//酒店里的每 一个房间的信息
public class Room{
private int no;
//房间编号 一楼;101,102,103
// 二楼;201,202,203.。。。
private String type;
//房间类型;单人间,标准间,总统套房;
private boolean status;
//房间状态;true表示空闲,false表示满了
public Room() {
}
public Room(int no, String type, boolean status) {
this.no = no;
this.type = type;
this.status = status;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public boolean getStatus() {
//get方法访问赋值。
return status;
}
public void setStatus(boolean status) {
//set方法修改赋值
this.status = status;
}
//重写toString方法;将对象转换为字符串形式,而不是地址
// equals方法,比较两个对象是否相等
@Override
public String toString() {
return "Room{" +
"房间号" + no +
", 房间类型'" + type + '\'' +
", 房间状态" + (status ? "空闲":"占用") + //三目运算符
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Room room = (Room) o;
return no == room.no &&
status == room.status &&
Objects.equals(type, room.type);
}
@Override
public int hashCode() {
return Objects.hash(no, type, status);
}
}
一、String字符串概念?(java.lang.String类)
①在java中使用双引号括起来的都是String对象,凡是双引号括起来的,都在方法区内存的“字符串常量池”存有一份备份;
②String字符串在方法区常量池一旦创建就无法被改变(类似final修饰后的常量),
因此字符串如果是相同的话就会多次被指向一个相同的方法区内存地址, 要改变指向只能重新再创建一个别的字面值的字符串。
二、字符串String类对象储存位置?
1、String双引号的字符串和其他对象(堆内)不同,String字符串是存储在“方法区内存”的“字符串常量池(放字符串)”中。
(注意jdk7之后,字符串常量池被移到了堆内存中)
①例如;String s3=“abc”;
创建1个对象1个地址(在方法区内存的字符串常量池中) ----》
s1引用名保存和指向的是方法区常量池内存的对象地址,不是具体abc对象。
② String s3 = new String(“abc”);
创建2个对象2个地址(分别在堆内存中 和 方法区内存常量池中),
s3引用名保存和指向的先是堆内存的对象地址,再指向方法区常量池的对象地址。
s3引用——》new String对象堆内存地址——>?方法区常量池地址(abc)
三、String字符串注意点:
1、所有的局部引用变量其实都是存的对象内存地址而不是具体值,而不是像基本类型存的是具体数值。(超级重点)
① int i = 100; i变量是一块单元空间存的是100具体属性值
②User u = new User(“123”); u引用指向的是堆内存的Uer类对象的哈希码16进制地址(盒子),盒子同时再去放放具体的对象属性值123;
③String s1 = “abc”; s1引用存的是方法区常量池对象地址;
④String s2 = new String(“abc”);
s2引用指向的是堆内存的对象内存地址(盒子),但这个盒子不放东西,而是再去指向方法区常量池的地址(再放具体的String常量值)。
2、在方法区内存常量池地址中创建的String对象,若对象在常量池中已存在,则不需再次创建,直接多个引用指向同一个常量池地址即可。
3、String字符串引用名或者字面值对象,都可以直接去调方法 例如:
“HelloWorld”.toString()
“HelloWorld”.equals(s3);
四、String字符串对象比较:
String类型是特殊的对象类 ,既可以用= =号可以用equals 但有条件限制。
【1】先回顾一下:
Java中“==” 双等号(关系运算符)比较的是一个值,比如基本数据类型变量的字面值 和 对象的内存地址哈希值(输出的true/false),
而equals方法比较的是引用对象的具体内容的属性(包括String引用类型)是否相等。(只要内容属性相等就相等,不管形式)(return true/false)
【2】String特殊在于:String字符串在方法区常量池一旦创建就无法被改变(类似final修饰后的常量),
因此字符串字面值如果是相同的话就会多次被指向一个相同的方法区内存地址, 要改变指向只能重新再创建一个别的字面值的字符串。
【3】案例String 中使用 = = 和 equals的案例?
①String s3=“abc”; 和String s4=“abc”; 【这里只有一个内存地址】
这里只比较的一个相同方法区地址值,因为对于String字符串字面值如果是相同的话就会多次被指向一个相同的方法区常量池内存地址,
因此此时可以用 ==号去比较内存地址值,但也可以调用String类已经重写好父类Object类的equals方法(equals更方便快捷)
②String s3 = new String(“abc”);和 String s4 = new String(“abc”);
【这里一共有三个内存地址】
这里比较的的对象地址是,两个堆内存地址和一个方法区内存地址,
因为两个new String类对象会在堆内存会变为两个不同的地址了,而方法区内存常量池的地址中还是只有一个abc。
因此综上所述,此时是不能用==号, 必须只能调用String类已经重写好父类Object类的equals方法。
③总结,最好比较任何引用对象大小都用equals方法比较。(省去很多麻烦事,但了解一下是必须的)
public class SUN公司String类已经重写好了Object类中的toString和equals方法 {
public static void main(String[] args) {
String s1 = "abc"; //一个内存地址
String s2 = "abc";
String s3 = new String("abc"); //两个内存地址
String s4 = new String("abc");
String s6 = "Hello"; //(equals就是用来判断地址具体内容是否一样的)
String s7 = "World";
String s8 = "HelloWorld";
String s9 = "Hello" + "World";
String s10 = s6+s7;
System.out.println(s1 == s2); //结果为true,
System.out.println(s1.equals(s2)); //结果也为true,
System.out.println(s3==s4); //结果为false,
System.out.println(s3.equals(s4)); //true
System.out.println(s1==s3);//false
System.out.println(s1.equals(s3));//true 比较的具体对象内容
System.out.println(s2.equals(s4));//true 比较的具体对象内容
System.out.println(s8 ==s6 +s7);//false, s8保存的常量池地址和(s6+s7)保存的常量池地址肯定不一样,直接false 0x1111== 0x222+0x333 比较的地址肯定不相等
System.out.println(s8 ==s10);//false 一样false,和上面一样
System.out.println(s8 == s9);//true //如果一定是想要引用名相加 s6+s7, 必须则先创建具体内容字符串的合体空间,再拼接;例如s9
System.out.println(s8=="Hello"+"World");//true。常量名相加 "en"+"joy",则
//可以先拼接,然后在常量池中找拼接体常量是否在s8的地址里面有,如果有不用再次创建即true,否则创建即false;
System.out.println(s8.equals(s6+s7));//true,因为用equals去比两个不同地址里面的具体属性内容来判断是否相等。内容"HelloWorld"= "Hello"+"World"的确相等。
System.out.println(s8.equals("Hello"+"World"));//true
String s5 = new String("广外南国商学院fat man");//SUN公司String类已经重写好了toString和equals方法,直接调用equals方法或者也可以不用引用也能直接访问了。
System.out.println(s5.toString());//经过测试可以直接输出结果,说明String已经重写了toString方法。
//如果String类没有重写toString方法,则这里就会输出,java.lang.String@十六进制地址。
System.out.println(s5);//重写了toString可以直接用引用调,会自动调toString方法,结果是一样的。
}
}
五、String类的构造方法(可以放什么)?
1、String的构造方法:常用的是参数可以是;
①不new,直接String s = “字面值对象”;(最常用)
②放“String字符串对象字面值”、
③中放char[]、byte[]等 (为什么能放,因为有String内部有其写好的方法)
String s = “对象”;最常用,
String s = new String(“对象”);
String s = new String(byte数组名);
String s = new String(char数组名);
1、基本方法学习!(都要记一下了解)
/*
1、String方法学习;(只是让你去大致了解这些方法,,真正要用自己去找帮助文档)
① " ".charAt(1);方法 通过下标来寻找字符串内的某个具体字符,并返回一个char类型接收 char c ="字符串". charAt(int index);
② " ".compareTo("abc");方法 通过对比两个字符串大小,(字符串比较不能用<>=)并返回int类型。 int c1="abv".compareTo("abc"); (注意;大于0说明左边比右边大,小于0右比左大,等于0一样大。)
compareTo方法和equals方法区别,一个不仅可以比较是否相等还能比较字符串大小(返回int),一个比较对象地址里具体内容是否相等(返回boolean)。
⑤ " ".equals("Hello");方法 通过比较字符串具体内容对象是否相等,返回boolean类型 "Hello".equals("Hello")
④ " ".equalsIgnoreCase("hello")方法 通过判断字符串是否相等,并且忽略大小写 "Hello".equalsIgnoreCase("hello")
⑩ " ".startsWith("He") 通过判断是否以某个字符串为开始。返回类型为boolean "Hello".startsWith("He")
④ " ".endsWith("lo");方法 通过判断字符串是否是以后面的字符串为结尾的,返回值为boolean "Hello".endsWith("lo")
③ " ".contains("He");方法 通过判断字符串是否存在包含关系,返回值为boolean "Hello".contains("He")
⑦ " ".isEmpty());方法 通过判断字符串是否为空串, "abc".isEmpty();
这个空串不是指null,null会出现空指针异常。返回类型为boolean "" .isEmpty()
⑥ " ".indexOf("lo")方法 通过寻找子字符串在此字符串中第一次出现的索引 ,返回int类型下标 "Hello".indexOf("lo")
⑥" ".lastIndexOf("ll")方法 通过寻找子字符串在此字符串中最后一次出现的索引,返回int类型下标 "llolloll".lastIndexOf("ll")
⑧ " ".length();方法 判断字符串的长度,如果长度为0,也代表和.isEmpty());方法一样为空串。 "abc".length()
⑨ " ".replace("老字符","新字符"); 新字符替换字原来符串 "http://www.baidu.com".replace("http","https")
⑩" " .split("-");方法 以指定的字符为分隔符来进行分割,以String数组的形式接受(需要遍历) "1990-10-1".split("-")
⑨ " ".substring(3);方法 以某个下标元素开始输出字符串。 "Hello".substring(3)
⑨" ".toLowerCase();方法 将字符串中的大写转换成小写 "HELLO".toLowerCase()
⑨ " ".toUpperCase();方法 将字符串中的小写转换成大写 "hello".toUpperCase()
⑩ " ".trim();方法 去空格空白的 " da".trim()
下面三个特殊方法;
⑤" " .getBytes();方法 将"数字"字符串返回成对应的ACSII码byte数组 + 返回引用类型为数组(需要遍历) byte[] bytes="acv".getBytes();
⑨ " ".toCharArray();方法 将"字符"字符串转换成char数组 ,返回数组 "Hello".toCharArray();
⑩String.valueOf(10/ new Customer()); "非字符串(基本和对象)"转换字符串,是String类的唯一的静态方法,不需要创建String对象,如果要转对象,则new被转的对象即可。
(右看到左) 放对象类型/基本类型会自动调该对象(需重写)/包装类(已重写)的toString方法,
返回String字符串类型(因为任何参数进入valueOf方法后都会底层去再去调toString方法才会返回字符串)。
直接用String类名调,不需要newString对象.传什么参数都会自动调toString方法,因此需要重写该对象的toString方法,实际valueOf底层是toString方法;
传参数进入String类的valueOf方法源代码;
public static String valueOf(Object obj) { //传引用类参数转字符串的valueOf方法源代码。
return (obj == null) ? "null" : obj.toString(); //对象去toString返回变成字符串,在去valueof变为字符串
}
public static String valueOf(int i) { //传基本数据类型参数转字符串的valueOf方法源代码。
return Integer.toString(i); //i会(自动装箱为包装对象类)去toString方法把包装类具体的值i(相当于是拆箱)再返回变成字符串,在去valueof返回为字符串
}
重点;String.valueOf(10/ new Customer());(任何类型东西都能转字符串)
但是Integer.valueOf();(只能int和String类型)
Double.valueOf()(只能是int,String和Double)
以此类推
*/
package com.bipowernode.javaSE.String类方法汇总;
public class String字符串类常见方法 {
// 字符串常见的方法
public static void main(String[] args) {
char c="中国人".charAt(1);//索引下标的字符 //这里字符串对象可以直接.方法名-——去调方法 例如"HelloWorld".toString() /"HelloWorld".equals(s3);
System.out.println(c);//国
int c1="abv".compareTo("abc");
System.out.println(c1);//对比两个字符串 ,和equals方法差不多,一个比较字符串大小(返回int),一个比较对象地址里具体内容是否相等(返回真假)。
//a一样,b也一样,看v和c谁大,v比c大,则返回一个大于0的int数。一般比较第一个高位数就知道谁大了,如果相等就往后推一位继续比。
System.out.println("Hello".contains("He"));
//判断字符串是否存在包含关系,返回值为boolean
System.out.println("Hello".endsWith("lo"));
//判断字符串是否是以后面的字符串为结尾的,返回值为boolean
System.out.println("Hello".equals("Hello")); //true
//判断字符串是否相等
System.out.println("Hello".equalsIgnoreCase("hello"));
//判断字符串是否相等,并且忽略大小写
byte[] bytes="acv".getBytes();
//将字符串返回成byte数组
for(int i=0;i<bytes.length;i++){
System.out.println(bytes[i]); //97,98,118
}
System.out.println("Hello".indexOf("lo")); //3下标
//子字符串在此字符串中第一次出现的索引
System.out.println("llolloll".lastIndexOf("ll")); //6下标
//子字符串在此字符串中最后一次出现的索引
String c4="";
System.out.println(c4.isEmpty());//true
System.out.println( "abc".isEmpty());//false
//判断字符串是否为空串,这个空串不是指null,null会出现空指针异常
System.out.println("abc".length()); //3
//字符串的长度
System.out.println("http://www.baidu.com".replace("http","https"));
//新字符替换字原来符串
String[] str="1990-10-1".split("-");
//以指定的字符为分隔符来进行分割,以String数组的形式接受
for(int i=0;i<str.length;i++){
System.out.println(str[i]);
}
System.out.println("Hello".startsWith("He"));
//判断是否以某个字符串为开始
System.out.println("Hello".substring(3));
//以下标元素开始输出字符串
char[] chars="Hello".toCharArray();
//将字符串转换成char数组
for(int i=0;i<chars.length;i++){
System.out.println(chars[i]);//H,e,l,l,o.
}
System.out.println("HELLO".toLowerCase());
//将字符串中的大写转换成小写
System.out.println("hello".toUpperCase());
//将字符串中的小写转换成大写
System.out.println(" da".trim()); //去掉空格的 输出"da"
//去空格空白的
String i=String.valueOf(10);//int类型转字符串
//"非字符串(基本和引用类型)"转换字符串,是String类的唯一的静态方法,不需要newString对象.放引用类型/基本来兴会自动调该对象/包装类的toString方法,
System.out.println(i);//10 基本类型转字符串
Customer customer = new Customer();
String s = String.valueOf(customer); //传对象引用类型参数转为对象转字符串类型,
System.out.println(s); //10
//这里valueOf(customer)底层一样会调customer的toString无参方法,和下面是一样的
System.out.println(customer); //10
//这里和上面其实也一样,底层一样是调用customer的toString方法,是同一样的结果,只是一个静态一个实例
}
class Customer{
@Override
public String toString() {
return String.valueOf(10); // 第一种直接把int通过valueOf转为字符串即可
//第二种在return 后面写"10";
}
}
//这里会再次套娃,进入public static String valueOf(int i) { return Integer.toString(i);}
2、Java中obj引用的 toString(无参) 和String.valueOf(有参) 的区别:
String str1 = obj引用.toString(); VS String str1 = String类.valueOf(obj对象/int基类);
toString(无参)和valueOf(有参)的区别就是;
1、大体讲:
①valueOf底层其实就是toString(无参/有参),能变"基本类型"和"obj对象"为String形式。
②而toString(无参)是只让obj对象变为字符串形式输出的方法)
③而valueOf(有参)实际包含着一部分的toString(无参)方法。
④共同点作用:都有一个相同的功能那就是可以把对象,转换和返回成String字符串形式输出
2、细致讲:
①一个无参数(内部的return转),实例方法要new对象,仅仅是改变对象自身输出形式的方法(里面是可以自己手写改对象的输出形式,但一定要字符串形式);
②一个有参数(直接形参外部转),静态方法用类调,将特定的“参数”转为字符串String,其实底层也是会去调"基类的包装类或者obj对象“重写的toString(无/有参)方法.
③因此综上:String.valueOf()有着独特优点:就是还可以把"基本类型"转换字符串String,而toString(无参)不能。
>PS注意1:通过方法转型如果obj为null不会抛出NullPointerException异常;
>而是将obj对象转换成"null"字符串。这样 str1 就被赋值为 "null",
>这样乍一看没什么问题,感觉这样可以避免NullPointerException异常,如果后期要对str1要做其他类型的转换就会出问题。
比如:Integer.valueOf(str1);这是就会报错NumberFormatException提示数据类型转换出错(因为返回的字符串“null”无法转去Integer)。
到这里就会发现 String.valueOf()埋下的坑了。
所以一般情况下在使用这两个方法前最后都要考虑一下null值得情况,不然后期发现问题,改数据将是非常痛苦的。
PS注意2:**jdk里String.valueOf(Object/int i)源码如下:(底层是toString(有参/有参))
① public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString(); }
//底层是toString(无参),调obj对象的toString(无参)方法(自定义类对象的话记得要自己重写toString)
②public static String valueOf(int i) {
return Integer.toString(i);}
//底层是toString(有参),调基数的包装类的toString(有参)方法(不需要重写)
③而Object的toString()源码如下:
public String toString() {
//仅仅只能是对象
return getClass().getName() + '@' + Integer.toHexString(hashCode());
}
> PS注意3:这种方法套方法的套路:其实是一样的不要怕!
> //正常写法:
Customer customer = new Customer();
String s = String.valueOf(customer);
System.out.println(s);
//这里valueOf(customer)底层一样会调customer的toString无参方法,和下面是一样的
System.out.println(customer); //这里和上面其实也一样,底层一样是调用customer的toString方法,是同一样的结果,只是一个静态一个实例
//方法套方法写法:
public String toString() {
//把对象以字符串的形式输出;
return String.valueOf(11); // 这里底层就是再去调一次Integer包装类的
//toString(有参)转为字符串,返回即可。其实没变,也可以直接手打"id = " + id;
}
public String toString() {
//把对象以字符串的形式输出;
return String.valueOf(new Vip(11));
//这回会报错, Exception in thread "main" java.lang.StackOverflowError
//因为String.valueOf(new Vip(11))实际底层会去调toString无参,即这个上面这个重写的public String toString()方法
//那么就会进入递归,如果没有条件限制,就会死循环栈溢出报错。
}
六、StringBuffer和StringBuilder类(字符串拼接类)
1、StringBuffer和StringBuilder类概念?
(底层是byte[]数组缓冲区,开辟一定容量byte数组,将字符串通过getBytes方法转成byte类型,最后再存进byte数组里面,不够会自动扩容)。
2、作用:
都适用于字符串的“频繁拼接” ,两个都是字符串拼接的工具类
(相当于是一个字符串缓冲区,也叫,长度容量为16的byte[]数组,把字符串放到缓存区里面拼接。)
2、为什么StringBuffer append方法 比 String + 的拼接效率高?
(可以看看append方法底层源代码)
①因为字符串的字符在常量池一旦创建,是不可变的,因此每次创建String对象都要消耗大量内存,效率低,
②、StringBuffer类就是在字符串常量池中创建byte[]数组,容量是可变的。
(不会创建新的String对象,会自动调arraycopy方法扩容拷贝byte[]数组,来追加。),因此效率高。
ps:但是它的初始化容量是16为了效率,最好预先给一个合适的容量。
3、具体类里面的拼接方法------->append()方法!!!!
①s.append(“i”); 方法;
②里面的参数随便传递,作用是:追加拼接字符串,并且 s.append(“i”);方法不会创建新的String对象,效率高
③因此以后拼接字符串,尽量不要用+号,因为+号方法其实每次方法区常量池都会创建String对象,会一直消耗方法区常量池的消耗大量内存,效率低。
④尽量使用StringBuffer和StringBuilder类里面的append()方法;不会创建新的String对象,会自动调arraycopy方法扩容拷贝byte[]数组,在底层来追加。
4、特别注意:区别什么时候使用!!!!
【1】Stringbuffer时线程安全的
因为他的append方法会有同步锁(要synchronized排队,效率变低)
【2】Stringbuilder时线程非安全的(但数据可能不安全)
【3】单线程的时候一般使用stringbuilder,效率高一点
在多线程的时候一定要使用stringbuffer,保证线程数据安全
1、两个类的构造方法可以放什么(参数);
StringBuffer()/StringBuilder()
构造一个“没有字符的字符串缓冲区,初始容量为16个字符。” //字符串缓冲区也叫;长度容量为16的byte[]数组
StringBuffer/StringBuilder()(int capacity)
构造一个“没有字符的字符串缓冲区”和“指定的初始容量”。
StringBuffer/StringBuilder()(CharSequence seq)
构造一个包含与指定的相同字符的字符串缓冲区 CharSequence 。
StringBuffer/StringBuilder()(String str)
构造一个初始化为“”内容的字符串缓冲区。
2、四个构造方法的java源代码;
public StringBuffer() {
super(16); }
public StringBuffer(int capacity) {
super(capacity); }
public StringBuffer(CharSequence seq) {
this(seq.length() + 16);append(seq);}
public StringBuffer(String str) {
super(str.length() + 16);append(str);}
package com.bipowernode.javaSE.StringBuffer和StringBuilder类;
public class StringBuffer和StringBuilder {
public static void main(String[] args) {
String s1 = " "; //在外面的篮子,第一次循环s1为0+1,第二次循环,此时s1已经变为0+1,因此第三次为0+1+2,相当于数列
for (int i= 0; i < 100; i++){
/* s1 = s1 + i+" "; //直接用String +来追加拼接字符串 s1+=i;
System.out.println(s1); */
//以上String +方法会不断创建String对象,太占内存
StringBuffer s2 =new StringBuffer(1000);
System.out.println(s2.length());
s1+=i;
s2.append(s1);//用append追加
System.out.println(s2);
//StringBuffer类的追加方法不会创建新的String对象,会自动调arraycopy方法扩容拷贝byte[]数组,来追加。
}
System.out.println("---------------------------------------------");
//构造一个“没有字符的字符串缓冲区,初始容量为16个字符。
StringBuffer s =new StringBuffer(100);
s.append("i");
s.append(97);
System.out.println(s);//i97
// s.append("i");方法不会创建新的String对象,会自动调arraycopy方法扩容拷贝byte[]数组,来追加。
}
}
大总结:
String类和StringBuffer和StringBuilder操作字符串面试题!
也可以看看这位大佬的String类型为什么是不可变的
一、为什么String类型字符串是不可变的???
① 因为我看过源代码,String类的底层有一个byte[]数组,而这个byte[]数组是被final修饰过的
②数组一旦创建长度不可变,因此引用类型的数组一旦被final修饰之后,去指向的盒子对象内容也不可以再变。
③数组长度不可变,同时内容也不可变,因此String字符串一旦创建不可再变。(不是指;不能再次赋值,而是指方法区常量池的字符串不能被改变,除非重新创建另一个新的字符串)
④ String+方法去拼接字符串;表面是可以直接+,但实际会不断在常量池中创建new新String对象,浪费常量池太多内存。
二、为什么StringBuffer/StringBuilder类型可以变?
①因为我看过源代码,StringBuffer类的底层也有一个byte[]数组,而这个byte[]数组没有被final修饰过的。
②并且数组容量初始值为16,虽然长度不可变,但是当内存容量满了会自动扩容,调用拷贝方法System.arrraycope(){}
③所以StringBuffer/StringBuilder类是可以自动扩容变化长度的,且不会创建新的String类型字符串,在底层拼接,适合字符串中的频繁拼接,不会浪费过多内存。而String+方法会不断创建新对象,浪费常量池太多内存。
一、包装类概念?(和String一样sun公司已经重写所有方法,使用起来比较方便)
1、java.lang.Number 中为8种基本数据类型又对应的准备了8种包装类型。8种包装类属于引用数据类型。
2、为啥要提供包装类呢?(作用)
调用方法时候想要传一个基本数据类型,但是被调方法形式参数又是对象Object,无法符合类型,因此把基本数据类型转为包装类,而包装类父类即是Number类又是Object,就可以传了。
3、包装类分类:(父类为Number类也继承了Object类,因此八大包装类也间接继承了Object类)
基本数据类型 包装类
byte lang.Byte(父类Number) Number有什么方法,子类包装类就有什么方法。
short java.lang.Short(父类Number)
int java.lang.Integer(父类Number) 特殊Integer
long java.lang.Long(父类Number)
float java.lang.Float(父类Number)
double java.lang.Double(父类Number)
boolean java.lang.Boolean(父类Number)
char java.lang.Character(父类Number) 特殊Character
其中有6种的父类都是Number类,Number类是一个抽象类
4、包装类(这里只举例Integer)就和String类一样都是对象级别的类,
①是引用数据类型,有自己的构造方法和对象。x,y,z都是引用名,保存的是对象的内存地址。而不是具体值。
②包装类和String类一样是sun公司已经写好的(包括toString和equals方法),因此直接输出引用即可访问。
5、包装类Integer的构造方法:这里主要学int类型包装类Integer(其他包装类照葫芦画瓢):
【1】
Integer x = int类型; (这种最常用)
//直接放int类型,输出即可 int类型 转为Integer 自动装箱
Integer y = new Integer(100);
//new对象构造方法放 int类型 转为Integer 手动装int箱
Integer z = new Integer(“123”);
//new对象构造方法放 String类型 转为Integer 手动装String箱
【2】重点构造方法的参数;必须只能为合法范围的整数类型
例如;一、123/"123"这样可以。
二、不能超过了int范围最大值,否则报错
三、不是合法整数,为别的类型, 会出现NumberFormatException数字格式化异常
四、类型不能比int大,为long或者double、float 。强转会出现精度损失;
包装类Integer的构造方法展示:
public class Integer类构造方法 {
public static void main(String[] args) {
Integer x = 123; //自动装箱,直接放int类型.相当于Integer x = new Integer("123");
Integer y = new Integer(100); //手动装箱。new对象,int包装为 Integer,
Integer z = new Integer("123"); //new对象,String 包装为 Integer
Double d = 1.23;// 相当于Double d1 = new Double("1.23"); //自动装箱
Double d1 = new Double("1.23");//手动装箱
Double d2 = new Double(1.23); //手动装箱
String s1 = new String("100");
String s2 = "100";
System.out.print(x+" ");
System.out.print(y+" ");
System.out.print(z+" ");
System.out.println(); //两种换行方法:第一是先全部不换,一次性输出完再整体
System.out.print(d+" ");
System.out.print(d1+" ");
System.out.println(d2+" "); //都不换行,最后一个换即可
System.out.print(s1+" ");
System.out.println(s2+" ");
}
}
123 100 123
1.23 1.23 1.23
100 100
二、包装类的自动装箱和拆箱。
1、(自从jdk1.5之后就有了自动装箱和自动拆箱,支持包装类和基本类相互自动转换)
①自动拆箱就是指将“包装类”----->基本数据类型.
②自动装箱就是指将“基本数据类型”------>成包装类.
2、意义;更加方便 ,可以不用学父类Number类和子类Integer的某些方法了,
例如:手动装箱或者拆箱方法淘汰过时了,因为可以自动装箱。
包装类.valueOf(100); Integer.valueOf(100); //手动装箱,静态方法
类引用.intValue()/floatValue()/byteValue()/longValue();//手动拆箱,实例方法
Integer.valueOf(100).floatValue();//先装后拆
3、注意:Integer 和 String 细微区别?(Integer可以在堆内存or可以在常量池)
【1】包装类Integer这个对象如果调取[-128~127]之内,就和String类一样
①sun公司在类加载已经把Integer类的[-128~127]一共256个对象提早放去了常量池)
②java项目中把这种“提前加载数据”叫做“缓存机制cache”,优点;优化项目效率高 缺点;稍微耗费点内存
Integer i1 = new Integer(127); 两个个对象new Integer 和 127(一个堆内存地址和一个常量池地址)
Integer i2 = new Integer(128); 和正常类一样;一个堆内存地址对象为128
String s = new String(“Hello”);两个对象new String和"Hello"(一个堆内存地址和一个常量池地址)
【2】包装类Integer如果在[-128~127]范围在之外,就和正常的类一样,需要去在堆内存中new创建新对象,然后再引用指向的是堆内存的对象;
Integer i1 =127; 一个常量池对象地址
Integer i2 = 128;一个堆内存对象地址;
String s = “hello”;一个常量池对象地址;
【3】总结注意点:
①String不管是什么范围什么值,具体对象都是在存在方法区的常量池中。且对象一旦创建就不可变,可以多次被引用指向。
②因此总结比较Integer对象时要注意,取值范围。看是否能用==号,还是equals方法。
4、装箱(类型必须一致) 和 拆箱(可拆为不一致类型)方法的理解汇总:
Integer i = 100; //int类型—>包装类型(这种叫自动装箱)
Integer i2 = new Integer(100);//第一种手动装箱,手动new对象
Integer i3= Integer.valueOf(100);//第二种叫手动装箱方法,用 Integer.valueOf方法
int c = i; //包装类型—>int类型(这种叫自动拆箱)
int c2 = new Integer(100).intValue();//第一种对应的手动拆箱方法,用xx.intValue();方法
int c1 =Integer.valueOf(123).intValue();//第二种对应的手动拆箱;用xx.intValue();方法
自动装箱和拆箱代码展示:
public class 自动装箱和拆箱 {
public static void main(String[] args) {
//装箱
Integer i2 = new Integer(100);//第一种手动装箱,手动new对象
Integer i3= Integer.valueOf(100);//第二种叫手动装箱方法,用 Integer.valueOf方法
Integer i = 100; //int类型--->包装类型(这种叫自动装箱)
System.out.println(i3);//100 是一个对象
System.out.println(i);//100 可以直接输出对象引用,已经重写好了toString方法
System.out.println(i2);//100
//拆箱
int c2 = new Integer(100).intValue();//第二种对应的手动拆箱方法,用i.intValue();方法
int c1 =Integer.valueOf(123).intValue();//第二种对应的手动拆箱;用i.intValue();方法
int c = i; //包装类型--->int类型(这种叫自动拆箱)
System.out.println(c2);//100 是一个字面值
System.out.println(c1);//100
System.out.println(c);//100
//综合;先装后拆(先装为int包装类,后拆为float基本类型)
Integer.valueOf(100).floatValue();
new Integer(100).intValue();
System.out.println( Integer.valueOf(100).floatValue());
System.out.println( new Integer(100).intValue());
//自动装拆箱后的新特性语法;
Integer i1 = 1000;
System.out.println(i1+100);//有了自动装箱和拆箱之后,java承认这种语法,对象+基本类型
//因为i1和基本数类相加时,会自动拆为了基本数据1000,因此支持i1+1
System.out.println(i.equals(c));//有了自动装箱和拆箱之后,java承认这种语法,对象和字面值可以比较,会自动转为同一类型。
System.out.println(i3==c);//有了自动装箱和拆箱之后,java承认这种语法,对象和字面值可以比较,会自动转为同一类型。
//Integer 对象对比 VS String 对象对比
Integer a = 1000;
Integer b = 1000;
System.out.println(a==b);//false ,因为a,b取值在[-128~127]范围在之外,此时在是Integer类对象是存在堆内存中。因此a和b的堆内存地址是不一样的。比较对象是否相等只能用equals。
System.out.println(a.equals(b));//true
String s1 = "123";
String s2 = "123";
System.out.println(s1==s2);//true ,但是区别于Integer,String的s1和s2不同,因为s1和s2是永远存在方法区常量池中的。被s1和s2同时指向一个“123”字符串对象。
System.out.println(s1.equals(s2));//true
//Integer[<-128||>127] 对象对比 VS Integer[-128~127] 对象对比
/*Integer这个对象如果调取[-128~127]之间的整数,就不需要通过new对象,引用直接去引用方法区的整数型常量池,
而如果不在这个[-128~127]范围,就需要去在堆内存中new对象*/
Integer x=128;
Integer y=128;
System.out.println(x==y);//false 这个取值的x,y对象在分别堆内存中。指向不同的地址,不能直接==比。要用equals方法
System.out.println(x.equals(y));
Integer i5=127;
Integer i4=127;
System.out.println(i5==i4);//true 这个取值的i3.i4对象在常量池中。指向的是同一个地址,可以直接==比。
System.out.println(i5.equals(i4));
}
}
包装类基本概念代码展示:
public class 包装类基本概念和意义 {
public static void main(String[] args) {
MyInt m1 = new MyInt(100); //原来;只能通过MyInt对象的构造方法来传对象,进入方法来间接传int类型属性,然后访问。
Integer i = new Integer(100); //现在;手动装箱(类型要一致)。把int基本数类参数100--->转为 Integer包装类。然后就可以直接把100作为一个类传进去。
float f = i.floatValue(); //手动拆箱方法(可以拆为不一致), Integer包装类装为float----> 基本数据类型;(此时100先包装,再拆装成float类型)
System.out.println(f); //100.0
dosome(m1);//100
// MyInt对象是;直接通过对象构造方法来间接传id属性100,向上转型(MyInt父类是Object),来转100进入(但要重写toString方法,比较麻烦)
dosome(i);//100
// Integer对象是;间接通过传int包装类对象,也是向上转型(Integer父类是Object),来传100进入。。。。(更直接一点,效率高,不用重写任何方法可以输出)
}
public static void dosome(Object obj){
//传m1,向上多态转型了。 Object obj = m1
System.out.println(obj); //100 向上转型了,实际是输出m1引用,重写toString方法即可
}
}
class MyInt {
int id;
public MyInt() {
}
public MyInt(int id) {
this.id = id;
}
@Override
public String toString() {
return String.valueOf(id); //第一种方法;调String.valueOf方法, 把int类型直接转为转字符串。
//第二种方法;用以前的方法,手写改return 一个字符串""+属性名
}
}
Integer常用类方法汇总:
int 和 String 和 Integer之间的转换方法汇总:
1、Integer有几个常用的方法(父类number也一样有):
①Integer.MAX_VALUE、Integer.MIN_VALUE 包装类对应基本数据类型的最大or最小值
①Integer.toBinaryString()、 10转为2进制字符串
②Integer.toHexString()、 10转为16进制字符串
③Integer.toOctalString() 10转为8进制字符串
④Integer.valueOf(123);/ Integer.valueOf("123") 装箱静态方法,将指定参数的String或int类型转为Integer类型,
⑥Integer.valueOf(123).intValue();和 x.intValue(); 拆箱实例方法 ,Integer类型转为int
⑥Integer.parseInt(String x); 静态方法,将指定的String类型转换为int类型, "100"-->100 静态方法,返回int类型
照葫芦画瓢;Double.parseDouble("100"); 直接可以把String字符串变为double基本类型
Float.parseDouble("100"); 直接可以把String字符串变为float基本类型
⑦Integer.toString(int i);//把指定的int类型转为字符串String类型, 100-->"100" 静态方法,返回String类型
包装类.toString(int i) 不等于 引用.toString(无参);一个类型转换、一个输出对象的方法
2、大总结;int 和 String 和 Integer之间的转换方法;
(看方法先看方法名是什么功能的方法,不要先看类名,类名只能决定是哪个类的方法,再去看参数是什么转什么)
(基本都是用valueOf转换类型的方法,如果看到valueOf才可以直接看类名,类型转换
int--->String四种方法: 1、String.valueOf(int); 2、Integer.toString(int i);(toString有参转为字符串)
3、数字+" " ( 字符串拼接) 4、StringBuilder/StringBuffer(append方法)
String--->int: Integer.parseInt("123");(特殊:前提是String必须是int类型字面值,返回一个int)。
类似String变为Integer 然后再变回int
int--->Integer:Integer.valueOf(123); 装箱方法
Integer--->int:integer引用.intValue(无参); 拆箱方法
String--->Integer:Integer.valueOf("123"); 装箱, 把String字符串装箱Integer
Integer--->String:String.valueOf(Integer对象);(特殊) String方法, 把Integer引用对象转为字符串
*/
public class Integer类常用方法 {
public static void main(String[] args) {
Integer x = Integer.valueOf("123");//123装箱方法
System.out.println(x);
System.out.println(x.toString());//toString让对象以字符串形式输出。只是一种改变输出形式而已。
//Integer.valueOf("123");是会改变数据的类型的,基本类变为包装类;
int i1 = x.intValue();//123拆箱方法
int i = Integer.valueOf(100).intValue();//100拆箱
System.out.println(i1); //123
System.out.println(i); //100
int i2 = Integer.parseInt("123");
System.out.println(i2); //String转int类型
String s = Integer.toString(123);
System.out.println(s); //第一种int转String
String s1 = String.valueOf(123);
System.out.println(s); //第二种int转String
double d = Double.parseDouble("100"); //照葫芦画瓢 String字符串变为double基本类型
System.out.println(d);
String s2 = String.valueOf(new Integer(123));//Integer对象(int类型)变为String
String s3 = String.valueOf(new Integer("123"));//Integer对象(字符串)变为String
System.out.println(s2);
System.out.println(s3);
}
}
了解:Integer的valueof方法和intValue方法和parseInt方法区别
总结详细解析:
一、Integer.parseInt(String s );方法
是String 转为 int 的方法之一。(前提是String必须是int类型字面值)
Integer.parseInt("1")=1;
Integer.parseInt("20")=20;
Integer.parseInt("324")=324; 当然,s 表示的整数必须合法,不然是会抛异常的。
二、Integer.valueOf(“123”).intValue(); /引用..intValue();
是Integer类"实例拆箱方法",是类Integer的实例方法,返回的也是int基本数据类型;
三、Integer.valueOf(int/String ), 【右看向左】
是Integer类"静态装箱方法",它的作用“int/String转为”包装为包装类(装箱方法。)和intValue();相反
四、而之前的String.valueOf(int/obj)底层其实就是toString(有/无参),能变"基本类型"和"obj对象"为String形式。
package com.bipowernode.javaSE.String类方法汇总;
public class Integer的valueof方法和intValue方法和parseint方法区别 {
public static void main(String[] args) {
Integer i =Integer.valueOf(123); //第一种手动装箱方法
Integer i1 = new Integer(123); //第二种手动装箱方法
Integer i2 = 123;//第三种自动装箱方法
Integer.valueOf(123).intValue();//对应的第一种拆箱
new Integer(123).intValue();//对应的第二种拆箱方法
Integer.parseInt("123");//类似于拆箱方法,String类拆为int类型
int i3 =i;//自动拆箱方法
System.out.println(i);
System.out.println(i1);
System.out.println(i2);
System.out.println(i3);
}
}
一、日期类概念:(sun公司写好的日期类)
1、常用日期类:(sun固定写死的类)
java.util.Date类 获取电脑系统日期工具类;
java.util.calendar类 日历类
java.text.SimpleDateFormat类 负责日期格式化工具类;
2、总结Date类知识点:
知识点1、 获取当前系统Date类时间: new Date对象输出即可
知识点2、 Date日期对象---->String自定义形式日期 :用Simple类的format方法,返回String字符串类型输出形式。
知识点3、 String自定义形式日期----> Date日期对象: 用Simple类的parse方法,返回的Date对象
3、注意点:
①为什么要format日期格式化?
因为Date类已经是sun写好的源代码,因此无法自己重写toString方法去修改输出结果,因此只能格式化日期格式然后输出
【1】如果直接输出date引用,内部重写好的toString则会输出一堆英文字符串: Fri Dec 25 13:13:36 CST 2020 (但也有好处可以看出星期几)
【2】而使用format方法类似就是对象的toString方法,把Date对象输出为自己想要的字符串String形式 (符合中国人审美)
②如果Simple类构造方法里的日期格式没有遵守修改规则,可能就会出异常或者数据不匹配
错误1: SimpleDateFormat s = new SimpleDateFormat(“abc”); (报错异常了) java.lang.IllegalArgumentException:
错误2: SimpleDateFormat s2 =new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss:sss”);
“2000-12-20 10:12:10:340” —》2000-12-20 10:17:40:040 (数据不匹配了)
正确写法: SimpleDateFormat s = new SimpleDateFormat(“yyyy.MM.dd HH:mm:ss:SSS”);
一小一大写法: (年.月.日 时-分-秒-毫秒)
import java.text.SimpleDateFormat;
import java.util.Date;
public class Date无参构造获取当前日期和修改日期 {
public static void main(String[] args) throws Exception {
//获取当前系统的时间
Date today= new Date();
System.out.println(today);//Fri Jul 03 17:59:18 CST 2020 不符合中国人表达习惯;
//但是Date已经是sun写好的源代码,又无法自己重写toString方法去修改输出结果,因此只能日期格式化
//那么日期可以格式化么?(就是按照自己的指定,把日期格式化)
//java.text.SimpleDateFormat 负责日期格式化
//类中格式化方法SimpleDateFormat的“新日期格式参数”规则;
/*一小一大 (年月日,时分秒毫秒)
yyyy;年(年是四位数)
MM:月
dd;日
HH;时
ss;秒
SSS;毫秒(1000毫秒等于1秒)
注意;在新日期格式中,除了y、M、d、H、s、S不能随便改,其他"拼接"格式可以随便改
例如;"yyyy-MM-dd HH:mm:ss:SSS" 可以用1999-10-1 20:08:18:005
"yyyy/MM/dd HH:mm:ss:SSS" 可以1999/10/1 20:08:18:005
"dd/MM/yyyy HH:mm:ss:SSS" 可以1/10/1999 20:08:18:005
"yyyy.MM.dd HH:mm:ss:SSS"
*/
//第一:默认格式的日期Date类----------》变为自定义格式的字符串时间
// 用Simple类的format方法,返回一个Date对象的字符串类型输出形式。
Date today1= new Date();
SimpleDateFormat s = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss:SSS"); //在"Simple类构造中"修改和格式化日期的类格式(构造方法参数类型为String字符串类)
String newDate =s.format(today1);//调用Simple类的format方法,放当前时间date进去修改格式,返回新的字符串日期格式
System.out.println(newDate);//2020-09-28 01:05:59:167
//上面这个结果会不断变化的,因为是一开始获取的是当前的系统时间(一直在变的)
//案例二:自定义格式的自定义String时间--------> 指定时间的Date类对象? (就可以获得自己心中想要的时间的date类对象)
// 用Simple类的parse方法,返回指定时间的Date对象
SimpleDateFormat s2 =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
String 自定义date = "2020-12-25 10:12:10:340"; //这里为自定义的String字符串时间。(注意;不能乱写,转的字符串日期要和类构造方法日期格式一致,否则java.text.ParseException分析异常)
Date date2 = s2.parse(自定义date);
System.out.println(date2); //Fri Dec 25 10:12:10 CST 2020 直接输出的话会是系统默认格式的。(但是有好处可以看出星期几)
System.out.println( s2.format(date2));//2000-12-20 10:12:10:340 建议format转为自定义格式好看点 (符合中国人视觉习惯)
//类似重写toString
}
}
二、Date日期类的有参构造方法:
1、Date类的有参构造方法参数表示的是(long类型的毫秒) :1000毫秒=1秒。
public Date(long dtae) {
fastTime = date;
}
2、
【1】 要是Date是无参构造,则对象输出的就是当前电脑系统的时间;
【2】要是Date是有参构造,则对象输出sun公司默认时间 + long毫秒参数 ;
①英国格林尼治本初子午线是:(Thu Jan 01 00:00:00 CST 1970)+ long毫秒
②但是在中国是东八区北京时间: Thu Jan 01 08:00:00 CST 1970 +long毫秒
package com.bjpowernode.javaSE.日期类.关于Date类及其格式化;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Date有参构造方法 {
public static void main(String[] args) {
Date date = new Date(2000);
System.out.println(date); //Thu Jan 01 08:00:02 CST 1970
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
String s = sdf.format(date);//放现在时间修改自定义格式
System.out.println(s);//1970-01-01 08:00:02:000
//获取昨天此时的时间;并且修改好格式 1秒=1000毫秒
Date date2 = new Date(System.currentTimeMillis()-1000 * 60 * 60 * 24);//(用当前时间的总毫秒数)-(一天的毫秒数)=前一天的总毫秒数
String s2 = sdf.format(date2);// 修改日期
System.out.println(s2); //2020-09-27 15:39:38:078
//昨天 VS 今天
//获取当天当前的时间,并且修改好了格式
Date date3 = new Date();
String s3 = sdf.format(date3);
System.out.println(s3);//2020-09-28 15:39:38:078
//还可以获取去年、前年、后年今天的时间
//自己去玩
}
}
Thu Jan 01 08:00:02 CST 1970
1970-01-01 08:00:02:000
2020-12-24 13:33:12:056
2020-12-25 13:33:12:057
三、学习一下一个静态方法:System.currentTimeMillis();
1、
【1】默认功能;
计算出从1970年1月1日的00;00;00到现在的所需时间总和(是毫秒数)(在中国北京是东八区,是从1970年1月1日的 08;00;00开始计算)
【2】额外功能; (计算一个程序运行所耗费的时长)
2、long x = System.currentTimeMillis();方法,返回值是long类型。单位是毫秒。1秒=1000毫秒。这个方法可以计算出程序执行的时间:
在程序begin开始的毫秒数 - over结束的毫秒数=程序执行的时间。
3、具体如何统计一个方法耗时?4个步骤:
1、在调用方法开始之前记录一个毫秒数;
2、调用方法,运行程序
3、在调用方法结束之后记录一个毫秒数
4、最后把结束时间-开始时间= 中途程序执行所耗费的时长
package com.bjpowernode.javaSE.日期类.关于Date类及其格式化;
public class 计算程序运行所需时长 {
public static void main(String[] args) {
long requiredTime = System.currentTimeMillis();
System.out.println(requiredTime);//1601261434541 计算出从1970年1月1日的00;00;00到现在的所需时间总和(毫秒数),单位是毫秒。1秒=1000毫秒
//流程;如何统计一个方法耗时?4个步骤
//1、在调用方法开始之前记录一个毫秒数;
long begin=System.currentTimeMillis();
//2、调用方法,运行程序并开始计时。
print();
//3、在调用方法结束之后记录一个毫秒数;
long over=System.currentTimeMillis();
//4、最后把结束时间-开始时间= 中途程序执行所耗费的时长
System.out.println("耗费时长"+(over-begin)+"毫秒");//注意每次的毫秒数都会不一样,因为这看电脑CPU处理速度的
}
//项目需求;计算一个方法程序执行所所需要耗费的时长;例如下面的print方法
public static void print(){
for(int i=0;i<1000 ;i++){
System.out.println(i);
}
}
}
四、Date日期类和Calender日历类联用:
Calender类具体去看:嘿嘿嘿
package com.bjpowernode.javaSE.日期类.关于Date类及其格式化;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class Calender日历类2 {
public static void main(String[] args) throws Exception{
//取得今天的日期
Date today = new Date();
System.out.println(today);
//格式化日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(today));
//联合Calender类
Calendar c = Calendar.getInstance();
System.out.println(c.get(Calendar.DAY_OF_MONTH)); //当前月的第几天
//取得 2000-10-01 为星期几
Date d = new SimpleDateFormat("yyyy-MM-dd").parse("2000-10-01");
//这里是装比写法,其实就是simple引用对象的parse方法而已,返回一个自己想要的时间Date对象
c.setTime(d); // 将Date对象表示的时间值设置给Calendar日期对象。
System.out.println(c.get(Calendar.DAY_OF_WEEK)); //返回的是当前周的第几天,
//星期天是第一天,也就是星期几即结果要减1
System.out.println(d); //Sun Oct 01 00:00:00 CST 2000
}
}
/*
Fri Dec 25 12:31:01 CST 2020
2020-12-25 12:31:01
25
1
Sun Oct 01 00:00:00 CST 2000
*/
一、Math数学类在java.lang.Math下,
1、它提供了一系列静态方法用于科学计算,其方法的参数和返回值类型一般为double型。 如果需要更加强大的数学运算能力,计算高等数学中的相关内容,可以使用apache commons下面的Math类库。
2、Math类的常用方法:
3、Math类中虽然为我们提供了产生随机数的方法Math.random();(只能产生0-1的随机数),但是通常我们需要的随机数范围并不是[0,1)之间的double类型的数据,这就需要对其进行一些复杂的运算。
4、因此如果使用Math.random()计算过于复杂的话,就可以不用Math类的random方法,我们可以使用另外一种方式得到随机数,即Random类,这个类是专门用来生成随机数的,并且Math.random()底层调用的其实就是Random的nextDouble()方法。
5、特别了解一下:java.math包下的BigDecimal小数高精度方法
注意java.math.BigDecimal 不等于上面的 java.lang.Math
*高精度数字类(BigDecimal对象类):
1、java.math.BigDecimal
这个是处理大数据使用的数学工具,精度极高,适用在银行等场景。
2、处理财务软件时候,double基本类型保留的小数点,其实是不够的,要用到BigDecimal引用数据类型;
BigDecimal b1=new BigDecimal(100/22.834234234/12.1111f); //参数可以是int、double、float,但是都是后面保留十几位小数点,精度极高
package com.bjpowernode.javaSE.日期类.关于数字格式化;
import java.math.BigDecimal;
public class BigDecimal高精度数字格式化 {
public static void main(String[] args) {
BigDecimal b1=new BigDecimal(22.834234234); //参数可以是int、double、float,但是都是后面保留十几位小数点,精度极高
BigDecimal b2=new BigDecimal(234.4564523);
//b1+b2两个引用不能直接+,要调用add方法求和 b1.add(b2);
BigDecimal b3=b1.add(b2);
System.out.println(b3);//257.29068653399999533348818658851087093353271484375 精度极高
//b1+b2两个引用不能直接相除,要调用divide方法相除 b1.divide(b2);
BigDecimal b4 =b2.divide(b1);
System.out.println(b4);//java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
} //原来JAVA中如果用BigDecimal做除法的时候一定要在divide方法中传递第二个参数,定义精确到小数点后几位,
// 否则在不整除的情况下,结果是无限循环小数时,就会抛出以上异常。
//正常的数不会出现这个异常,放心。
}
6、了解文本格式化工具类:(有处理日期格式和处理数字格式的)
【1】位置:
日期格式:java.text.SimpleDateFormat:(上面讲了)
数字格式:java.text.DecimalFormat: (这里详细讲)
【2】注意点:
①日期和数字的新格式:都是在各自的"类构造方法"中格式化修改日期/数字的格式(构造方法参数类型都为双引号的String字符串类);
②而具体放“实参日期/数字 ” 进去修改为新格式的,有各自类别的其他方法;
/*
一、格式化工具类:(有处理日期格式和处理数字格式的)
1、复习一下,处理日期的文本格式化工具类;
①位置在import java.text.SimpleDateFormat;
②在类构造中修改和格式化日期的类格式(构造方法参数类型为String字符串类);SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");’
③这个是放实参日期进去修改为新格式的方法;
作用;系统默认Date---->自定义的String时间 自定义的String时间----> 系统默认Date,
注意;方法;String 新date =s.format(date); Date date1 = s2.parse(自定义date);
注意;参数类型;只能为当前的日期类的对象date 参数类型;只能为和类构造格式一致的String类型
返回类型;返回新的字符串日期格式。 返回类型;返回新的字符串日期格式。
2、这个是处理数字的文本格式化工具类;
①位置在:java.text.DecimalFormat ;
②在类构造中修改和格式化数字的格式(构造方法参数类型为String字符串类); DecimalFormat d = new DecimalFormat("###,###.##"); //
③这个是放实参数字进去修改为新数字格式的方法;
注意;方法;String s1 = d.format(1234.56);
注意;参数类型;只能为和类构造格式一致的long/double类型
返回类型;返回新的字符串数字格式
二、数字格式化的格式(英文的标点符号)(放构造方法为字符串类型)
# 代表任意数字
,代表千分位
. 代表小数点
*/
package com.bjpowernode.javaSE.日期类.关于数字格式化;
import java.text.DecimalFormat;
public class DecimalFormat类去数字格式化转为特定字符串 {
public static void main(String[] args) {
DecimalFormat d = new DecimalFormat("###,###.##"); //
String s =d.format(12323441.31);//传数字进去format方法里面修改格式 注意这里是long或者double类型
System.out.println(s);//12,323,441.31
DecimalFormat d2 = new DecimalFormat("###,###.0000");//小数点指的是不够补上0
String s1 = d2.format(1234.56);
System.out.println(s1);//1,234.5600
}
}
具体Math类和Random类方法代码点击看此处文章
二、Random类
1、这个是关于随机数的工具在java.util.Random下
2、如何使用?
①先new Random随机类
②使用随机数的方法:nextInt()方法,表示产生下一个int类型数为xxx。
int i=r.nextInt(无参);-----> 不限范围随便出一个数
int i1 =r1.nextInt(int类型有参6);---->从0-5限制范围,随机选一个数,不包括6
3、Random类常用方法:
4、Rondom类基本使用的代码展示:
package com.bjpowernode.javaSE.日期类.关于数字格式化;
import java.util.Random;
public class Random类 {
public static void main(String[] args) {
Random r= new Random(); //new Random类。
int i=r.nextInt(); //调其中的nextInt的方法。
System.out.println(i);//随便出一个数。
Random r1 = new Random();
int i1 =r1.nextInt(6);
System.out.println(i1);//从0-5,随机选一个数,不包括6
}
}
5、Random类高级案例使用: (了解即可,这种抽奖机制)
package com.bjpowernode.javaSE.日期类.关于数字格式化;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Random;
public class Random类案例 {
public static void main(String[] args) {
/*
编写程序,生成5个不重复的随机数[0-100]。重复的话就重复生成。
最后,把这五个随机数放到数组里面,同样也要求数组这五个随机数不重复。
*/
//长度为5的int数组。默认值都是0
int[] array = new int[5];
//创建Random对象
Random r = new Random();
//循环,生成随机数
for (int i = 0;i<array.length;i++ ){
array[i] = -1;//为什么要遍历里面五个数都为默认值-1? 因为确保要盒子里不是0-101内的数字,这样就不会放随机数进去的时候,发现重复而无法将随机数放进数组盒子里
}
int index = 0;
while(index < array.length){
int num = r.nextInt(101);//生成0-100随机数
//下面这一步最重要,传方法返回boolean,看数组是否存在重复随机元素。
if (!contains(array,num)){
//这里额外写一个方法返回布尔,来判断array数组有没有这个随机数num,如果false就代表盒子没有这个随机数,就能就把随机数num放进去对应下标的数组里面。
array[index] = num;
index++;//index++放里面就可以,无限循环while,直到五个随机数能够不重复进入盒子。
} //如果放到if外面,这个while循环就只能够循环5次了。这样有重复也只能五次,五次之后随机数还没进完盒子里,盒子里就会出现-1的默认值,出bug
}
//最后判断完了,不不会出现重复的五个随机数,才遍历该数组
for (int i = 0;i<array.length;i++){
System.out.println("第"+i+"随机数是:"+array[i]);
}
}
/*
* 单独编写一个方法,这个方法专门用来判断数组中是否包含某个元素;
* @param array02 int数组 原来int数组传进
* @param key 元素 num随机数传进
* @param return true表示包含,false表示不包含
* */
public static boolean contains(int[] array02,int key){
//int[] array继承了上面的元数组array,int key继承了上面的随机数num。
// 第一种方法;包含某个元素方法会有bug,
//下面这个方案虽然用算法,效率高,但是有bug,出现重复数-1,因为排序。
/*//对数组进行排序
Arrays.sort(array);
//对数组进行二分法查找
//二分法查找结果,如果>=0说明存在这个元素,并且找到了。
int index = Arrays.binarySearch(array, key);//就是看array数组里面有没有key这个具体元素,并返回该的元素int类型下标
return index >= 0;//如果index >= 0成立,则表示true表示包含,小于0则false表示不包含*/
// 第二种方法;直接遍历一个一个筛选,无bug,但是效率慢。
for (int i = 0;i<array02.length;i++){
if (array02[i]==key){
return true; //如果数组的里面的值= 随机数元素 key 返回true 说明存在这个元素,不能放进盒子
}
return false; //返回false 说明数组没有这个随机元素,可以把随机数num放进去了
}
return false; //这里为什么也要写return false,因为上面只是if语句判断手否,但是最外层for一旦不符合i
//因此这里写 return false/或者 return true都行。因为这里只是为了for不满足条件而返回布尔,从而去结束该contains方法而已。
//上面的if (!contains(array,num),只会用到判断if里面的是true/false否。
}
}