学习目标:
- String类
- static关键字
- Arrays类
- Math类
一、String类(很重要)
1.1 String类概述
概述
在前面学习常量的时候介绍过Java中的常量类型:
整数、小数、字符、字符串、null、真假值 true 、 false
字符串常量,它在Java中不属于基本数据类型, 而是引用类型,也称为类类型。Java中使用String这个类描述字符串这种常量数据。
API中对java.lang.String类的描述:
java.lang.String
类代表字符串。Java程序中所有的字符串文字(例如"abc"
)都可以被看作是实现此类的实例。
String描述的字符串,是指所有在Java程序中使用双引号引用起来的数据都是一个字符串对象。
特点
1、注意:因为字符串在程序中的使用频率相当高,为了更方便对字符串的操作,java对字符串做了比较特殊的处理。字符串常量,它属于对象,但是它不是存储在堆空间中,而是存储在方法区中的字符串常量池中。字符串常量池中保存的就是所有的字符串数据。只要我们书写了双引号,不管双引号中间是什么数据,这些数据都会立刻在字符串常量池中保存。
2、字符串不变:字符串的值在创建后不能被更改。String类中描述的是所有字符串常量,一旦书写完成,它就是一个固定值,这个数据不能改变。
String s = "abc";
s += "d";
System.out.println(s); // abcd
// 内存中有"abc","abcd"两个对象,s从指向"abc",改变指向,指向了"abcd"。
3、因为String对象是不可变的,所以它们可以被共享。
//定义字符串对象
String s1 = "abc";
String s2 = "abc";
System.out.println( s1 == s2 );//这里比较的是内存地址名是否相等 结果为true
// 内存中只有一个"abc"对象被创建,同时被s1和s2共享。
“abc” 等效于
char[] data = { ‘a’ , ‘b’ , ‘c’ };
例如:
String str = "abc";
相当于:
char data[] = {'a', 'b', 'c'};
String str = new String(data);
// String底层是靠字符数组实现的。
String类中的api告诉我们字符串是常量不能被改变,这里的不能被改变意思是说在常量池中的字符串对象中的常量数据不能被改变,而指向这些字符串对象的引用变量空间中的内存地址值是可以改变的,这样可以让同一个String类型的引用变量空间指向方法区中字符串常量池中不同的字符串常量对象空间所保存的常量数据。
1.2 使用步骤
- 查看类
-
java.lang.String
:此类不需要导包。
- 查看构造方法
String类中提供了大量的构造方法,目的是可以帮助我们将其他的数据变成字符串对象。
-
public String()
:初始化新创建的 String对象,以使其表示空字符序列。相当于 String s=”“; -
public String(char[] value)
:通过该构造方法可以将参数的字符数组转换为String类的对象。 -
public String(byte[] bytes)
:通过该构造方法可以将参数的字节数组转换为String类的对象。 - 构造举例,代码如下:
// 无参构造
String str = new String();
// 通过字符数组构造
char chars[] = {'a', 'b', 'c'};
String str2 = new String(chars);
// 通过字节数组构造
byte bytes[] = { 97, 98, 99 };
String str3 = new String(bytes);
1.3 常用方法
判断功能的方法
public boolean equals (Object anObject)
:将此字符串与指定对象进行比较。
Object 是” 对象”的意思,是一种引用类型。作为参数类型,表示任意对象都可以传递到方法中。public boolean equalsIgnoreCase (String anotherString)
:将此字符串与指定对象进行比较,忽略大小写。
方法演示,代码如下:
public class String_Demo01 {
public static void main(String[] args) {
// 创建字符串对象
String s1 = "hello";
String s2 = "hello";
String s3 = "HELLO";
// boolean equals(Object obj):比较字符串的内容是否相同
System.out.println(s1.equals(s2)); // true
System.out.println(s1.equals(s3)); // false
//boolean equalsIgnoreCase(String str):比较字符串的内容是否相同,忽略大小写
System.out.println(s1.equalsIgnoreCase(s2)); // true
System.out.println(s1.equalsIgnoreCase(s3)); // true
System.out.println("-----------");
}
}
注意:
如果想比较两个字符串相等,我们不应该使用==(号),因为==是用来比较具体常量数值的。
而由于字符串是对象,所以我们应该使用String类中的equals方法对两个字符串进行比较,因为equals方法是用来比较两个对象是否相等。
获取功能的方法
public int length ()
:返回此字符串的长度。public String concat (String str)
:将指定的字符串连接到该字符串的末尾。public char charAt (int index)
:返回指定索引处的 char值。public int indexOf (String str)
:返回指定子字符串第一次出现在该字符串内的索引。public String substring (int beginIndex)
:返回一个子字符串,从beginIndex开始截取字符串到字符串结尾。public String substring (int beginIndex, int endIndex)
:返回一个子字符串,从beginIndex到endIndex截取字符串。含beginIndex,不含endIndex。
方法演示,代码如下:
public class String_Demo02 {
public static void main(String[] args) {
//创建字符串对象
String s = "helloworld";
// int length():获取字符串的长度,其实也就是字符个数
System.out.println(s.length());
System.out.println("--------");
// String concat (String str):将将指定的字符串连接到该字符串的末尾.
String s = "helloworld";
String s2 = s.concat("**hello itheima");
System.out.println(s2);// helloworld**hello itheima
// char charAt(int index):获取指定索引处的字符
System.out.println(s.charAt(0));
System.out.println(s.charAt(1));
System.out.println("--------");
// int indexOf(String str):获取str在字符串对象中第一次出现的索引,没有返回-1
System.out.println(s.indexOf("l"));
System.out.println(s.indexOf("owo"));
System.out.println(s.indexOf("ak"));
System.out.println("--------");
// String substring(int start):从start开始截取字符串到字符串结尾
System.out.println(s.substring(0));
System.out.println(s.substring(5));
System.out.println("--------");
// String substring(int start,int end):从start到end截取字符串。含start,不含end。
System.out.println(s.substring(0, s.length()));
System.out.println(s.substring(3,8));
}
}
转换功能的方法
public char[] toCharArray ()
:将此字符串转换为新的字符数组。public byte[] getBytes ()
:使用平台的默认字符集将该 String编码转换为新的字节数组。public String replace (CharSequence target, CharSequence replacement)
:将与target匹配的字符串使用replacement字符串替换。
方法演示,代码如下:
public class String_Demo03 {
public static void main(String[] args) {
//创建字符串对象
String s = "abcde";
// char[] toCharArray():把字符串转换为字符数组
char[] chs = s.toCharArray();
for(int x = 0; x < chs.length; x++) {
System.out.println(chs[x]);
}
System.out.println("-----------");
// byte[] getBytes ():把字符串转换为字节数组
byte[] bytes = s.getBytes();
for(int x = 0; x < bytes.length; x++) {
System.out.println(bytes[x]);
}
System.out.println("-----------");
// 替换字母it为大写IT
String str = "itcast itheima";
String replace = str.replace("it", "IT");
System.out.println(replace); // ITcast ITheima
System.out.println("-----------");
}
}
分割功能的方法
-
public String[] split(String regex)
:将此字符串按照给定的regex(规则)拆分为字符串数组。
方法演示,代码如下:
将字符串”aa,bb,cc” 以逗号进行切割。
public class String_Demo03 {
public static void main(String[] args) {
//创建字符串对象
String s = "aa,bb,cc";
String[] strArray = s.split(","); // ["aa","bb","cc"]
for(int x = 0; x < strArray.length; x++) {
System.out.println(strArray[x]); // aa bb cc
}
}
}
1.4 String类的练习
拼接字符串
定义一个方法,把数组{1,2,3}按照指定个格式拼接成一个字符串。格式参照如下:[word1#word2#word3]。
public class StringTest1 {
public static void main(String[] args) {
//定义一个int类型的数组
int[] arr = {1, 2, 3};
//调用方法
String s = arrayToString(arr);
//输出结果
System.out.println("s:" + s);
}
/*
* 写方法实现把数组中的元素按照指定的格式拼接成一个字符串
* 两个明确:
* 返回值类型:String
* 参数列表:int[] arr
*/
public static String arrayToString(int[] arr) { [1#2#3]
// 创建字符串s
//String s = new String("[");
String s = "[";
// 遍历数组,并拼接字符串
for (int x = 0; x < arr.length; x++) {
if (x == arr.length - 1) {
s = s+arr[x]+"]";
} else {
s = s+arr[x]+"#";
}
}
return s;
}
}
统计字符个数
键盘录入一个字符串,统计字符串中大小写字母及数字字符个数.
说明:从键盘获取字符串使用Scanner类中的String next()方法.
public class StringTest2 {
public static void main(String[] args) {
//键盘录入一个字符串数据
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串数据:");
String s = sc.nextLine();
//定义三个统计变量,初始化值都是0
int bigCount = 0;
int smallCount = 0;
int numberCount = 0;
//遍历字符串,得到每一个字符
for(int x=0; x<s.length(); x++) {
char ch = s.charAt(x);
//拿字符进行判断
if(ch>='A'&&ch<='Z') {
bigCount++;
}else if(ch>='a'&&ch<='z') {
smallCount++;
}else if(ch>='0'&&ch<='9') {
numberCount++;
}else {
System.out.println("该字符"+ch+"非法");
}
}
//输出结果
System.out.println("大写字符:"+bigCount+"个");
System.out.println("小写字符:"+smallCount+"个");
System.out.println("数字字符:"+numberCount+"个");
}
}
二、 static关键字
2.1 概述
static
关键字它是一个修饰符,它可以用来修饰类的成员变量和成员方法,被修饰的成员是属于类的,而不是单单是属于某个对象的。也就是说,既然属于类,就可以不靠创建对象来调用了。可以直接使用类名调用。
2.2 定义和使用格式
类变量
当 static
修饰成员变量时,该变量称为类变量。该类的每个对象都共享同一个类变量的值。任何对象都可以更改该类变量的值,但也可以在不创建该类的对象的情况下对类变量进行操作。
- 类变量:使用 static关键字修饰的成员变量。
定义格式:
static 数据类型 变量名;
举例:
static int number;
调用格式
// 访问被静态的类变量
类名.静态类变量名; // 不需要通过对象调用访问
2.3 static变量练习
需求:描述圆这类事物,并定义功能,能够计算出圆的面积。
/*
描述一个圆,计算圆的面积
*/
class Circle{
//圆的半径
private double r;
//圆周率
private double PI = 3.14;
//构造方法
public Circle(double r){
this.r = r;
}
//对外提供一个计算圆面积的方法
public double getArea(){
return r * r * PI;
}
}
public class CircleTest{
public static void main(String[] args) {
//创建圆的对象
Circle c1 = new Circle(4);
//使用对象调用计算圆面积的方法
double area1 = c1.getArea();
System.out.println("area1="+area1);
//创建圆的对象
Circle c2 = new Circle(6);
//使用对象调用计算圆面积的方法
double area2 = c2.getArea();
System.out.println("area2=" + area2);
}
}
上述程序有问题:
创建的所有圆对象中都一个相同的属性数据PI,而这个数据会随着对象的创建在堆中开辟空间,对象越多,开辟的内存空间就会越多。既然这个成员变量所有的对象都共享,这时我们可以让这个变量在内存中只开辟一次,然后让所有对象共享这个变量。
要解决这个问题,就可以通过静态关键字来修饰这个成员变量,当某个成员变量被静态关键字修饰了,这个成员变量就变成所有对象共享的一个变量,并且这个变量所在的内存空间也发生了改变。
完善后的代码如下:
/*
描述一个圆,计算圆的面积
*/
class Circle{
//圆的半径
double r;
//圆周率
static double PI=3.14;
//构造函数
public Circle(double r){
this.r = r;
}
//对外提供一个计算圆面积的函数
public double getArea(){
return r *r * PI;
}
}
练习:
我们可以这样定义一个静态变量numberOfStudent,代码如下:
class Student {
private String name;
private int age;
// 学生的id
private int sid;
// 类变量,记录学生数量,分配学号。未分配之间为0,没分配一个学号增加1
public static int numberOfStudent = 0;
// 构造方法
public Student(String name, int age){
this.name = name;
this.age = age;
// 通过 numberOfStudent 给学生分配学号
this.sid = ++numberOfStudent;
}
// 打印属性值
public void show() {
System.out.println("Student : name=" + name + ", age=" + age + ", sid=" + sid );
}
}
public class StuDemo {
public static void main(String[] args) {
Student s1 = new Student("张三", 23);
Student s2 = new Student("李四", 24);
Student s3 = new Student("王五", 25);
Student s4 = new Student("赵六", 26);
s1.show(); // Student : name=张三, age=23, sid=1
s2.show(); // Student : name=李四, age=24, sid=2
s3.show(); // Student : name=王五, age=25, sid=3
s4.show(); // Student : name=赵六, age=26, sid=4
}
}
静态方法
当static
修饰成员方法时,该方法称为类方法 。静态方法在声明中有static
,建议使用类名点来调用,而不需要创建类的对象。调用方式非常简单。
- 类方法:使用 static关键字修饰的成员方法,习惯称为静态方法。
定义格式:
修饰符 static 返回值类型 方法名 (参数列表){
// 执行语句
}
举例:
public static void show() {
System.out.println("shwo....run...");
}
调用格式
// 调用静态方法
类名.静态方法名(参数列表); // 不需要通过对象调用
代码演示:描述人这类事物
/* 创建一个Person类 */
class Person{
//成员变量
String name;//姓名
int age;//年龄
//成员方法
public void sleep(){
System.out.println("睡觉。。。。");
}
public void speak(){
System.out.println("姓名=" + name + ",年龄=" + age);
}
}
public class PersonTest{
public static void main(String[] args) {
//创建Person类的对象
Person p1 = new Person();
//使用p1对象调用Person类中的sleep函数
p1.sleep();
p1.name = "张三丰";
p1.age = 120;
p1.speak();
Person p2 = new Person();
//使用p2对象调用Person类中的sleep函数
p2.sleep();
}
}
相比下面例子
Java中提供的static关键字可以用来修饰方法,当某个方法被static关键字修饰之后就会变成类方法,在调用的时候就可以通过类直接调用,而不需要依赖于对象了。
class Student{
public static void sleep(){
System.out.println("睡觉。。。。。。");
}
}
public class StudentTest {
public static void main(String[] args) {
// 调用静态方法
Student.sleep();
}
}
小结:
1、 被static修饰的成员方法,称为静态成员方法。
2、 通常访问静态方法的方式是通过类名来访问。
3、 访问静态成员方法的格式: 类名.静态方法名(参数列表)。
4、 只要一个方法被static关键字所修饰,那么这个方法就和对象没有关系了,只和类有关系。
5、 在静态方法中不能使用非静态成员变量。(因为对象还没创建)
静态方法注意事项(重要)
1:调用方式
静态方法,可以使用类名直接调用。
非静态成员方法,需要使用对象调用。
2、静态关键字是一个修饰符。可以修饰类中的成员方法和成员变量。但是不能修饰构造方法。
因为构造方法的作用是用来创建对象给对象初始化,一旦被static修饰,就不需要对象调用。那么构造方法就和对象没关系了。没对象的话,构造方法就没价值了。
3、静态方法它是在类加载的时候,就在内存中加载完成,可以直接运行的方法。
非静态方法,它的运行必须是在类加载完成之后,通过new关键字创建出对象之后,通过对象才能调用。
4、静态方法中不能调用非静态方法。
因为静态方法在类加载完成之后可以通过类名直接调用,而类加载完了不代表类中产生了对象。非静态方法必须依赖于对象才能运行。
5、非静态方法中是可以调用静态方法的。
当非静态方法可以运行的时候,在内存中就一定有对象产生,既然有对象了,就说明对象所属的那个类肯定已经被加载完成了。类都加载完成了,静态方法就已经准备就绪。
6、静态方法中不能使用this 关键字。
this关键字它表示的是当前调用方法的那个对象。而在静态方法中是没有对象的。
2.4 静态原理图解
JVM在从硬盘上加载class文件的时候,这个class文件中的静态成员需要加载到方法区的静态区中。而所有的非静态成员和构造方法,都需要加载到方法区的非静态区中。
说明:
1、jvm虚拟机将class文件从硬盘上加载到方法区中,对于非静态变量,不是开辟内存空间,只是将被翻译成为二进制的代码加载到方法区内存中。
2、jvm虚拟机将class文件从硬盘上加载到方法区中,对于静态变量,jvm虚拟机会在方法区中给静态变量开辟空间,并且先给默认初始化值,然后在显示赋值。
代码如下:
//静态的内存加载
class Demo{
int x ;
static int y = 3;
public static void print(){
System.out.println("y="+y);
}
public void show(){
System.out.println("x="+x + ",y=" + y);
}
}
class StaticDemo {
public static void main(String[] args) {
Demo.print(); // 静态调用print方法
Demo d = new Demo();// 创建Demo对象
d.x = 10; // 给变量x赋值
d.show(); // 调用运行show()方法
}
}
静态和非静态成员变量的区别
1、它们在内存中出现的时间不同
静态成员变量:它是在加载类的时候,就在方法区的静态区中存在。
非静态成员变量:当创建类的对象的时候,随着对象的产生在堆中出现。
2、它们所在的内存区域不同
静态成员变量:在方法区的静态区中。
非静态成员变量:对象所在的堆内存中。
3、它们的初始化时间不同
静态成员变量:在类加载的时候就会初始化,类加载完成,变量已经初始化结束。
非静态成员变量:它是在对象的创建过程中被初始化。
4、它们的生命周期不同:
静态成员变量:它随着类的加载在方法区的静态区中存在。直到类被从方法区卸载,才会消失。
非静态成员变量:它是随着对象的产生而存在,随着对象的消失就消失。
2.5 静态总结
static 修饰的内容:
是随着类的加载而加载的,且只加载一次。
存储于一块固定的内存区域(静态区),可以直接被类名调用。
它优先于对象存在,所以,可以被所有对象共享。
方法使用静态: 当方法中没有访问到成员变量的时候,使用static来修饰方法
变量使用静态: 当类中的变量被所有对象共享的时候,使用static修饰在加载类的时候开辟空间
2.6 静态代码块
代码块:使用大括号把代码封装起来,被封装的这部分代码就称为一个代码块。
- 静态代码块:定义在成员位置,使用static修饰的代码块{ }。
- 位置:类中方法外。和成员方法并列,和先后顺序无关。
- 执行:随着类的加载而执行且执行一次。
格式:
public class ClassName{
//静态代码块
static {
// 执行语句
}
}
作用:给类变量进行初始化赋值。用法演示,代码如下:
public class Game {
public static int number;
public static ArrayList<String> list;
static {
// 给类变量赋值
number = 2;
list = new ArrayList<String>();
// 添加元素到集合中
list.add("张三");
list.add("李四");
}
}
class文件的加载过程
1、在加载class文件的时候,JVM会先加载类中的所有静态成员( 方法,变量,静态代码块 )都加载到方法区class文件的所处静态区中
2、当把所有的静态成员加载完成之后,开始给类中的所有静态成员变量进行默认初始化
3、当类中的所有静态成员变量默认初始化之后,接着开始给所有静态成员变量显示赋值。
4、当类中所有的静态成员变量显示赋值结束之后,静态代码块才会运行。
5、当静态代码块执行结束之后,才表示class文件加载完成
代码演示:
class StaticCode{
static int y = show();
static int x = 10;
static {
System.out.println("静态代码块运行....x = " + x);
}
public static int show() {
System.out.println("show...x = " + x);
return 100;
}
public void print() {
System.out.println("print run.....");
}
}
public class StaticCodeDemo {
public static void main(String[] args) {
new StaticCode().print();
}
}
小贴士:
static 关键字,可以修饰变量、方法和代码块。在使用的过程中,其主要目的还是想在不创建对象的情况下,去调用方法。下面将介绍两个工具类,来体现static 方法的便利。
三、 Arrays类
3.1 概述
java.util.Arrays
此类包含用来操作数组的各种方法,比如排序和搜索等。其所有方法均为静态方法,调用起来非常简单。这个是工具类。后面还会学到collections
3.2 操作数组的方法
-
public static String toString(int[] a)
:返回指定数组内容的字符串表示形式。
public static void main(String[] args) {
// 定义int 数组
int[] arr = {2,34,35,4,657,8,69,9};
// 打印数组,输出地址值
System.out.println(arr); // [I@2ac1fdc4
// 数组内容转为字符串
String s = Arrays.toString(arr);
// 打印字符串,输出内容
System.out.println(s); // [2, 34, 35, 4, 657, 8, 69, 9]
}
-
public static void sort(int[] a)
:对指定的 int 型数组按数字升序进行排序。
public static void main(String[] args) {
// 定义int 数组
int[] arr = {24, 7, 5, 48, 4, 46, 35, 11, 6, 2};
System.out.println("排序前:"+ Arrays.toString(arr)); // 排序前:[24, 7, 5, 48, 4, 46, 35, 11, 6, 2]
// 升序排序
Arrays.sort(arr);
System.out.println("排序后:"+ Arrays.toString(arr));// 排序后:[2, 4, 5, 6, 7, 11, 24, 35, 46, 48]
}
四、 Math类
4.1 概述
java.lang.Math
类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。类似这样的工具类,其所有方法均为静态方法,并且不会创建对象,调用起来非常简单。
4.2 基本运算的方法
-
public static double abs(double a)
:返回 double 值的绝对值。
double d1 = Math.abs(-5); //d1的值为5
double d2 = Math.abs(5); //d2的值为5
-
public static double ceil(double a)
:返回大于等于参数的最小的整数。
double d1 = Math.ceil(3.3); //d1的值为 4.0
double d2 = Math.ceil(-3.3); //d2的值为 -3.0
double d3 = Math.ceil(5.1); //d3的值为 6.0
-
public static double floor(double a)
:返回小于等于参数最大的整数。
double d1 = Math.floor(3.3); //d1的值为3.0
double d2 = Math.floor(-3.3); //d2的值为-4.0
double d3 = Math.floor(5.1); //d3的值为 5.0
-
public static long round(double a)
:返回最接近参数的 long。(相当于四舍五入方法)
long d1 = Math.round(5.5); //d1的值为6.0
long d2 = Math.round(5.4); //d2的值为5.0