JAVA基础学习笔记

写在前面:教程参考自HOW2J.CN,感谢站主的辛苦付出。

变量类型


Java变量的八种基本类型,都是预定义好的,且都是关键字。

整型 (4种)

字符型 (1种)

只能存放一个字符,值用单引号表示,双引号表示字符串,和short一样是16位。

public class HelloWorld{
  public static void main(String[] args){
	  char c = '中';
	  //char 只能存放一个字符,超过一个字符就会产生编译错误
	  char c2 = '中国'; //报错
	  char c3 = 'ab'; //报错
  }
}

浮点型 (2种)

float 长度为32位,double 长度为64位   注意: 默认的小数值是double类型的
所以 float f = 54.321会出现编译错误,因为54.321的默认类型是 double,其类型 长度为64,超过了float的长度32。
在数字后面加一个字母f,直接把该数字声明成float类型
float f2 = 54.321f

布尔型(1种)

public class HelloWorld {

	public static void main(String[] args) {

		boolean b1 = true;
		boolean b2 = false;

		// 虽然布尔型真正存放的数据是0(false) 1(true)
		// 但是,不能直接使用0 1 进行赋值
		boolean b3 = 1;

	}
}

 String 类型

String类型其实并不是基本类型,但是它是如此广泛的被使用,常常被误以为是一种基本类型。
String类型是Immutable的,一旦创建就不能够被改变。

public class HelloWorld {
    public static void main(String[] args) {
    	String str = "Hello Java";
    }
}

变量的命名规则

  1. 数字 字母 $ _
  2. 第一个字符不能是数字
  3. 使用完整的单词命名而非缩写
  4. 不能使用关键字,但能包含关键字

final关键字

可以修饰变量、类、方法:只有一次赋值的机会。

Scanner的注意点

如果在通过nextInt()读取了整数后,再接着读取字符串,读出来的是回车换行:"\r\n",因为nextInt仅仅读取数字信息,而不会读取回车换行"\r\n".
所以,如果在业务上需要读取了整数后,接着读取字符串,那么就应该连续执行两次nextLine(),第一次是取走回车换行,第二次才是读取真正的字符串。

import java.util.Scanner;
  
public class HelloWorld {
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        int i = s.nextInt();
        System.out.println("读取的整数是"+ i);
        String rn = s.nextLine();
        String a = s.nextLine();
        System.out.println("读取的字符串是:"+a);
    }
}

操作符


特别注意:逻辑操作符和三元操作符

逻辑操作符

长路与 两侧,都会被运算
短路与 只要第一个是false,第二个就不进行运算了

长路或 两侧都会被运算
短路或 只要第一个是true的,第二个就不进行运算了

三元操作符

省时省力小妙招!

public class HelloWorld {
	public static void main(String[] args) {

		int i = 5;
		int j = 6;

		int k = i < j ? 99 : 88;
		// 相当于
		if (i < j) {
			k = 99;
		} else {
			k = 88;
		}
		System.out.println(k);
	}
}

swtich语句

switch可以使用byte,short,int,char,String,enum

public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        System.out.println("请输入月份");
        int month= sc.nextInt();
        switch (month){
            case 2:
            case 3:
            case 4:
                System.out.println("春天");
                break;
            case 5:
            case 6:
            case 7:
                System.out.println("夏天");
                break;
            case 8:
            case 9:
            case 10:
                System.out.println("秋天");
                break;
            case 11:
            case 12:
            case 1:
                System.out.println("冬天");
                break;
            default:
                System.out.println("erro");
        }
    }

关于循环


while和do while

结束外部循环的方法:

在外部循环的前一行,加上标签
在break的时候使用该标签
即能达到结束外部循环的效果

public class HelloWorld {
    public static void main(String[] args) {
         
        //打印单数     
    	outloop: //outloop这个标示是可以自定义的比如outloop1,ol2,out5
    	for (int i = 0; i < 10; i++) {  		
            for (int j = 0; j < 10; j++) {
            	System.out.println(i+":"+j);
            	if(0==j%2)  
            		break outloop; //如果是双数,结束外部循环
    		}			
		}    	
    }
}

关于数组


下面是关于Java中数组的小知识 ~

 数组分配空间,同时赋值

public class HelloWorld {
	public static void main(String[] args) {
		//写法一: 分配空间同时赋值
		int[] a = new int[]{100,102,444,836,3236};
		//写法二: 省略了new int[],效果一样
		int[] b = {100,102,444,836,3236};		
		//写法三:同时分配空间,和指定内容
		//在这个例子里,长度是3,内容是5个,产生矛盾了
		//所以如果指定了数组的内容,就不能同时设置数组的长度
		int[] c = new int[3]{100,102,444,836,3236};		
	}
}

反转数组写法

for (int i = 0; i < a.length/2; i++) {
            int middle = a[a.length-i-1];
            a[a.length-i-1] = a[i];
            a[i] = middle;
        }   

输出数组的三种方式

增强型for循环 for each 只能用于取值,不能修改数组值

Java中int的最大最小值

Integer.MAX_VALUE
Integer.MIN_VALUE

 复制数组的值

Arrays工具类的常用方法

数组复制、数组转为字符串、数组排序、数组搜索、判断数组是否相同、数组填充

  •  copyOfRange的第三个参数取不到
  •  搜索前要先排序

 

例题:对二维数组排序 

int[] array4 = Arrays.copyOfRange(array, 0, 3);
        int[][] array_2 = new int[5][5];
        for (int i=0; i<array_2.length; i++){
            for (int j=0; j<array_2[i].length; j++){
                array_2[i][j] = (int)(Math.random() * 100);
            }
        }
        int[] temp = new int[array_2.length * array_2[0].length];
        for (int i=0; i<array_2.length; i++){
            System.arraycopy(array_2[i], 0, temp, array_2[i].length*i, array_2.length);
        }
        Arrays.sort(temp);
        System.out.println(Arrays.toString(temp));
        for (int i=0; i<array_2.length; i++){
            array_2[i] = Arrays.copyOfRange(temp, i*array_2.length, (i+1)*array_2.length);
        }
        for(int i=0; i<array_2.length; i++)
            System.out.println(Arrays.toString(array_2[i]));

Java中的类与对象


方法重载

方法名一样,但是参数类型不一样。在调用方法的时候,会根据参数类型以及数量,自动调用对应的方法。

可变数量的参数

在方法中使用操作数组的方式来处理参数

public void attack() {
        System.out.println(name + " 进行了一次攻击 ,但是不确定打中谁了");
    }
 
    // 可变数量的参数
    public void attack(Hero... heros) {
        for (int i = 0; i < heros.length; i++) {
            System.out.println(name + " 攻击了 " + heros[i].name);
 
        }
    }

构造方法/构造器

  • 实例化是通过构造方法实现的,方法名和类名一样,实例化一个对象的时候,必然调用构造方法。构造方法也可以重载。
  • 创建一个子类对象也要调用父类的构造函数。
  • this关键字相当于普通话里的“我”,即代表当前对象。可用通过this在一个构造方法中调用另一个构造方法。
//带一个参数的构造方法
    public Hero(String name){
        System.out.println("一个参数的构造方法");
        this.name = name;
    }
      
    //带两个参数的构造方法
    public Hero(String name,float hp){
        this(name);
        System.out.println("两个参数的构造方法");
        this.hp = hp;
    }

将相近的类划分到一个包下,包内的其他类可以直接使用,要使用其他包下的类要import。

类之间的几种关系

自身,同包子类,不同包子类,同包类,其他类。

Java中的四种访问修饰符

 从作用域来看,public能够使用所有的情况。 但是大家在工作的时候,又不会真正全部都使用public,那么到底什么情况该用什么修饰符呢?

  • 1. 属性通常使用private封装起来
  • 2. 方法一般使用public用于被调用
  • 3. 会被子类继承的方法,通常使用protected
  • 4. package用的不多,一般新手会用package,因为还不知道有修饰符这个东西

作用范围最小原则

简单说,能用private就用private,不行就放大一级,用package,再不行就用protected,最后用public。 这样就能把数据尽量的封装起来,没有必要露出来的,就不用露出来了。

静态属性

当一个属性被static修饰的时候,就叫做类属性,又叫做静态属性
当一个属性被声明成类属性,那么所有的对象,都共享一个值。

例如所有Hero的版权都相同,都属于同一家公司:

public class Hero {
    public String name; //实例属性,对象属性,非静态属性
    protected float hp;
    static String copyright;//类属性,静态属性
}

当一个对象改变该类属性时,由于共享同一块内存地址,其他对象的类属性也会跟着改变,实际工作中会加上final关键字

静态方法

静态方法,又叫类方法,直接通过类调用,例如Math.random()

初始化属性的三种方法的优先级:

  1. 声明时初始化
  2. 初始化块
  3. 构造方法初始化

单例模式

单例模式(面试点):Singleton模式,指的是在一个类、一个JVM中,只有一个实例存在。

三要素

  1. 私有化构造方法
  2. 静态属性指向实例
  3. public static的getInstance方法返回上一步的实例。

饿汉式单例模式

public class GiantDragon {
    private GiantDragon(){
        // 私有化构造方法使得无法在外部通过new实例化
    }
    // 准备一个类属性,指向该实例化对象
    private static GiantDragon instance  = new GiantDragon();
    // 提供给调用者获取上面的对象,静态方法允许不通过实例化对象访问
    public static GiantDragon getInstance(){
        return instance;
    }
}

懒汉式单例模式

public class GiantDragon {
  
    //私有化构造方法使得该类无法在外部通过new 进行实例化
    private GiantDragon(){       
    }
  
    //准备一个类属性,用于指向一个实例化对象,但是暂时指向null
    private static GiantDragon instance;
      
    //public static 方法,返回实例对象
    public static GiantDragon getInstance(){
        //第一次访问的时候,发现instance没有指向任何对象,这时实例化一个对象
        if(null==instance){
            instance = new GiantDragon();
        }
        //返回 instance指向的对象
        return instance;
    }
      
}

饿汉式属于立即加载,启动时可能会卡顿;懒汉式属于延迟加载,第一次调用会卡顿。

基于业务需求,如果业务上允许有比较充分的启动和初始化时间就用饿汉式,否则懒汉式。

枚举


Java枚举在项目中使用非常普遍,许多人在做项目时,一定会遇到要维护某些业务场景状态的时候,往往会定义一个常量类,然后添加业务场景相关的状态常量。 但实际上,生产环境的项目中业务状态的定义大部分是由枚举类来完成的,因为更加清晰明确,还能自定义不同的方法来获取对应的业务状态值,十分方便。

enum Season {
	SPRING,SUMMER,AUTUMN,WINTER
}
public class HelloWorld {
	public static void main(String[] args) {
		Season season = Season.SPRING;
		switch (season) {
		case SPRING:
			System.out.println("春天");
			break;
		case SUMMER:
			System.out.println("夏天");
			break;
		case AUTUMN:
			System.out.println("秋天");
			break;
		case WINTER:
			System.out.println("冬天");
			break;
		}
	}
}

枚举类的用法及相关函数

接口


接口:一种约定

实现某个接口:承诺了某种约定

package charactor;

public class ADHero extends Hero implements AD{

	@Override
	public void physicAttack() {
		System.out.println("进行物理攻击");
	}

}

Java 中 @Override 注解是用来指定方法重写的,只能修饰方法并且只能用于方法重写,不能修饰其它的元素。它可以强制一个子类必须重写父类方法或者实现接口的方法。

类型转换


类型转换:把一个引用所指向的对象的类型转换为另一个引用的类型。

对象是有类型的, 是ADHero
引用也是有类型的,是ADHero
通常情况下,引用类型和对象的类型是一样的
接下来要讨论的类型转换的问题,指的是引用类型和对象类型不一致的情况下的转换问题

  • 子类转父类(向上转型)可以;父类转子类(向下转型)需进行强制转换。没有继承关系的两个类,互相转换,一定会失败。
  • 实现类转换成接口(向上转型) 可以;接口转换成实现类(向下转型)

tip: instanceof Hero 判断一个引用所指向的对象,是否是Hero类型,或者Hero的子类

package charactor;
 
public class Hero {
    public String name; 
    protected float hp;
     
    public static void main(String[] args) {
        ADHero ad = new ADHero();
        APHero ap = new APHero();
        
        Hero h1= ad;
        Hero h2= ap;
        
        //判断引用h1指向的对象,是否是ADHero类型
        System.out.println(h1 instanceof ADHero);
        
        //判断引用h2指向的对象,是否是APHero类型
        System.out.println(h2 instanceof APHero);
        
        //判断引用h1指向的对象,是否是Hero的子类型
        System.out.println(h1 instanceof Hero);
    }
}

重写

子类可以继承父类的对象方法,在继承后,重复提供该方法,就叫做方法的重写,又叫覆盖 override。

多态

操作符的多态

同一个操作符在不同情境下,具备不同的作用:

  • 如果+号两侧都是整型,那么+代表 数字相加
  • 如果+号两侧,任意一个是字符串,那么+代表字符串连接

类的多态

同一个类型,调用同一个方法,却能呈现不同的状态。要实现类的多态,需要如下条件:

  1. 父类(或接口)引用指向子类对象
  2. 调用的方法有重写

方法的重写是子类覆盖父类的对象方法;隐藏就是子类覆盖父类的类方法。

super关键字

子类构造方法会默认调用父类的无参构造方法

子类可以使用super关键字显式调用父类带参的构造方法

可以通过super调用父类的属性被重写的方法

超类

Object类是所有类的父类,提供toString() finalize() equals() hashcode() getClass()方法

finalize()

当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件
当它被垃圾回收的时候,它的finalize() 方法就会被调用。
finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用的。

final关键字

final对于不同的概念有不同的意义:

  1. 修饰类表示不能被继承
  2. 修饰方法表明不能被重写
  3. 修饰基本类型变量表示只能被赋值一次
  4. 修饰引用表明只能指向一次对象
  5. 还用于修饰常量(public static final)

抽象类

  1. 当类中存在空方法时,该类需要有修饰符abstract;当一个类有抽象方法时,该类必须被声明为抽象类
  2. 继承抽象类的子类,必须实现抽象方法
  3. 一旦一个类被声明为抽象方法,就不能被直接实例化

抽象类与接口的区别

  1. 子类只能继承一个抽象类;但子类可以实现多个接口
  2. 抽象类可以定义 public,protected,package,private 静态和非静态属性 final和非final属性;但是接口中声明的属性,只能是 public 静态 final的,即便没有显式的声明

内部类


分为四种:非静态内部类、静态内部类、匿名类、本地类

非静态内部类

直接在一个类中定义,所以实例化BattleScore 的时候,必须建立在一个存在的英雄的基础上

语法: new 外部类().new 内部类()

内部静态类可以直接访问外部类的private实例属性的

package charactor;

public class Hero {
	private String name; // 姓名

	float hp; // 血量
	float armor; // 护甲
	int moveSpeed; // 移动速度

	// 非静态内部类,只有一个外部类对象存在的时候,才有意义
	// 战斗成绩只有在一个英雄对象存在的时候才有意义
	class BattleScore {
		int kill;
		int die;
		int assit;

		public void legendary() {
			if (kill >= 8)
				System.out.println(name + "超神!");
			else
				System.out.println(name + "尚未超神!");
		}
	}

	public static void main(String[] args) {
		Hero garen = new Hero();
		garen.name = "盖伦";
		// 实例化内部类
		// BattleScore对象只有在一个英雄对象存在的时候才有意义
		// 所以其实例化必须建立在一个外部类对象的基础之上
		BattleScore score = garen.new BattleScore();
		score.kill = 9;
		score.legendary();
	}

}

静态内部类

例如敌方水晶,当敌方水晶没有血时,己方所有英雄都取得胜利;静态内部类的实例化不需要一个外部类的实例为基础,可以直接实例化。

语法:new 外部类.静态内部类();

不可以访问外部类的实例属性和方法,只能访问私有静态成员

匿名类

匿名类指的是在声明一个类的同时实例化它,使代码更加简洁精练
通常情况下,要使用一个接口或者抽象类,都必须创建一个子类。有的时候,为了快速使用,直接实例化一个抽象类,并“当场”实现其抽象方法。
既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。

package charactor;
  
public abstract class Hero {
    String name; //姓名
    float hp; //血量
    float armor; //护甲
    int moveSpeed; //移动速度
     
    public abstract void attack();
     
    public static void main(String[] args) {
         
        ADHero adh=new ADHero();
        //通过打印adh,可以看到adh这个对象属于ADHero类
        adh.attack();
        System.out.println(adh);
         
        Hero h = new Hero(){
            //当场实现attack方法
            public void attack() {
                System.out.println("新的进攻手段");
            }
        };
        h.attack();
        //通过打印h,可以看到h这个对象属于Hero$1这么一个系统自动分配的类名
         
        System.out.println(h);
    }
     
}

本地类

本地类可以理解为有名字的匿名类。
内部类与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置。
本地类和匿名类一样,直接声明在代码块里面,可以是主方法,for循环里等等地方

package charactor;
  
public abstract class Hero {
    String name; //姓名
    float hp; //血量
    float armor; //护甲
    int moveSpeed; //移动速度
     
    public abstract void attack();
     
    public static void main(String[] args) {
         
        //与匿名类的区别在于,本地类有了自定义的类名
        class SomeHero extends Hero{
        	public void attack() {
                System.out.println( name+ " 新的进攻手段");
            }
        }
        
        SomeHero h  =new SomeHero();
        h.name ="地卜师";
        h.attack();
    }
     
}

在匿名类中使用外部的局部变量,外部的局部变量必须加final关键字(jdk8自动添加隐式final)
因为本地类中会声明一个与外部类局部变量的同名属性,并使用构造方法初始化该值,在匿名类中使用的该属性,实际上是内部声明的相同属性,而非外部属性。(其实笔者也不太明白)

匿名类练习:创建一个Item的匿名类,有抽象方法disposable()

public abstract class Item {
    String name;
    int price;
    public abstract void disposable();
    
}
class Main{
    public static void main(String[] args){
        Item i = new Item() {
            @Override
            public void disposable() {
                System.out.println("111");
            }
        };
        i.disposable();
    }
}

默认方法

默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法
Mortal 这个接口,增加了一个默认方法 revive,这个方法有实现体,并且被声明为了default

package charactor;

public interface Mortal {
	public void die();

	default public void revive() {
		System.out.println("本英雄复活了");
	}
}

为AD接口增加一个默认方法 attack()
为AP接口也增加一个默认方法 attack()
问: ADAPHero同时实现了AD,AP接口,那么 ADAPHero 对象调用attack()的时候,是调用哪个接口的attack()?

答案:作为同时继承了AD和AP中的 默认方法attack,就必须在实现类中重写该方法,从而免去到底调用哪个接口的attack方法这个模棱两可的问题。

接口内成员(属性、方法)定义

接口内属性的定义:接口中不能定义普通的属性,必须定义为常量,普通的类中可以用get set进行操作,接口不可以。

固定写法:public static final  数据类型 属性名 = 值;

(公共访问权限  静态的 固定)

封装类


所有基本类型都有对应的类类型,比如int的类是Integer

数字封装类(Byte,Short,Integer,Long,Float,Double)是抽象类Number类的子类

互相转换

        int i = 5;
 
        //基本类型转换成封装类型
        Integer it = new Integer(i);
         
        //封装类型转换成基本类型
        int i2 = it.intValue();

tip: 使用 = 进行自动装箱和自动拆箱

数字转字符串

  • 方法1: 使用String类的静态方法valueOf
  • 方法2: 先把基本类型装箱为对象,然后调用对象的toString
         //方法1
        String str = String.valueOf(i);
        //方法2
        Integer it = i;
        String str2 = it.toString();

字符串转数字

转floa的t方式和int类似 Float.parseFloat()

        String str = "999";
         
        int i= Integer.parseInt(str);
         
        System.out.println(i);

java.lang包

java.lang包提供了利用 Java 编程语言进行程序设计的基础类,lang是单词language的开头。该包由Java语言自动调用,不需要显示声明

java.lang.Math提供了一些常用数学运算,以静态方法存在

package digit;
 
public class TestNumber {
 
    public static void main(String[] args) {
    	float f1 = 5.4f;
    	float f2 = 5.5f;
    	//5.4四舍五入即5
    	System.out.println(Math.round(f1));
    	//5.5四舍五入即6
    	System.out.println(Math.round(f2));
    	
    	//得到一个0-1之间的随机浮点数(取不到1)
    	System.out.println(Math.random());
    	
    	//得到一个0-10之间的随机整数 (取不到10)
    	System.out.println((int)( Math.random()*10));
    	//开方
    	System.out.println(Math.sqrt(9));
    	//次方(2的4次方)
    	System.out.println(Math.pow(2,4));
    	
    	//π
    	System.out.println(Math.PI);
    	
    	//自然常数
    	System.out.println(Math.E);
    }
}

注意: 0和1不是质数。

格式化输出

System.out.printf() 和 System.out.format() 效果相同

%s 字符串 %d 整数 %n 换行

常用的格式化形式

next()和nextLine()区别

//next():只读取输入直到空格。
		String str = sc.next();
 
//nextLine():读取输入,包括单词之间的空格和除回车以外的所有符号
		String str2 = sc.nextLine();

char的封装类Character的常用方法

字符串转为字符数组

public class TestNumber {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        String str = input.next();
        char[] strArray = str.toCharArray();
        System.out.println(Arrays.toString(strArray));

    }
}

练习:创建一个长度是8的字符串数组
使用8个长度是5的随机字符串初始化这个数组
对这个数组进行排序,按照每个字符串的首字母排序(无视大小写)

注1: 不能使用Arrays.sort() 要自己写
注2: 无视大小写,即 Axxxx 和 axxxxx 没有先后顺序

package character;

import java.util.Arrays;

public class TestString {

	public static void main(String[] args) {

		String[] ss = new String[8];
		for (int i = 0; i < ss.length; i++) {
			String randomString = randomString(5);
			ss[i] = randomString;
		}
		System.out.println("未排序前的字符串数组:");
		System.out.println(Arrays.toString(ss));

		for (int j = 0; j < ss.length; j++) {
			for (int i = 0; i < ss.length - j - 1; i++) {
				char firstChar1 = ss[i].charAt(0);
				char firstChar2 = ss[i + 1].charAt(0);
				firstChar1 = Character.toLowerCase(firstChar1);
				firstChar2 = Character.toLowerCase(firstChar2);

				if (firstChar1 > firstChar2) {
					String temp = ss[i];
					ss[i] = ss[i + 1];
					ss[i + 1] = temp;
				}
			}
		}

		System.out.println("排序后的字符串数组:");
		System.out.println(Arrays.toString(ss));

	}

	private static String randomString(int length) {
		String pool = "";
		for (short i = '0'; i <= '9'; i++) {
			pool += (char) i;
		}
		for (short i = 'a'; i <= 'z'; i++) {
			pool += (char) i;
		}
		for (short i = 'A'; i <= 'Z'; i++) {
			pool += (char) i;
		}
		char cs[] = new char[length];
		for (int i = 0; i < cs.length; i++) {
			int index = (int) (Math.random() * pool.length());
			cs[i] = pool.charAt(index);
		}
		String result = new String(cs);
		return result;
	}
}

操作字符串

是否是同一个对象的特例

 判断字符串内容是否相同

  • 使用equals进行字符串内容的比较,必须大小写一致
  • equalsIgnoreCase,忽略大小写判断内容是否一致

判断是否以子字符串开始或结束

练习:创建一个长度是100的字符串数组 使用长度是2的随机字符填充该字符串数组 统计这个字符串数组里重复的字符串有多少种(忽略大小写)

package com.javase.day14.homework.hw4;

import java.util.Random;

public class Demo {
    public static void main(String[] args) {
        String[] strArr = new String[100];
        for (int i = 0; i < strArr.length; i++) {
            strArr[i] = getRandomString(2);
            System.out.print(strArr[i] + " ");
            if ((i + 1) % 20 == 0) System.out.println();
        }

        int[] intArr = new int[100];
        for (int i = 0; i < intArr.length; i++) {
            intArr[i] = 0;
        }

        for (int i = 0; i < strArr.length; i++) {
            if (intArr[i] == -1) continue;
            for (int j = i; j < strArr.length; j++) {
                if (strArr[i].equalsIgnoreCase(strArr[j])) {
                    intArr[i]++;
                    if (i != j) intArr[j] = -1;
                }
            }
        }

        for (int i = 0; i < intArr.length; i++) {
            if (intArr[i] > 1) {
                System.out.println(strArr[i] + "重复了" + intArr[i] + "次");
            }
        }
    }

    public static String getRandomString(int length) {
        String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(62);
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }
}

StringBuffer是可变长的字符串

方法:append delete insert reverse

public class TestString {
    public static void main(String[] args) {
        String str1 = "let there ";
        StringBuffer sb = new StringBuffer(str1); //根据str1创建一个StringBuffer对象
        sb.append("be light"); //在最后追加
        System.out.println(sb);
        sb.delete(4, 10);//删除4-10之间的字符
        System.out.println(sb);
        sb.insert(4, "there ");//在4这个位置插入 there
        System.out.println(sb);
        sb.reverse(); //反转
        System.out.println(sb);
    }
}

为什么StringBuffer可以变长?

和String内部是一个字符数组一样,StringBuffer也维护了一个字符数组。 但是,这个字符数组,留有冗余长度。

比如说new StringBuffer("the"),其内部的字符数组的长度,是19,而不是3,这样调用插入和追加,在现成的数组的基础上就可以完成了。

如果追加的长度超过了19,就会分配一个新的数组,长度比原来多一些,把原来的数据复制到新的数组中,看上去 数组长度就变长了 参考MyStringBuffer

属性length: “the”的长度 3
属性capacity: 分配的总空间 19

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值