Java的数据类型,一些鲜为人知的细节概述
Java的数据类型可以分成两大类:
-
基本数据类型
-
整型 byte,short,int,long
-
浮点型 float,double
-
字符类型 char
-
布尔类型 boolean
-
-
引用数据类型
-
数组
-
类
-
接口
-
这节主要是补充 Java的引用数据类型。
一、类
1、类的定义
-
具有相同属性和行为的对象可以抽象为类
-
类是一个封装了数据(属性)和操作这些数据的方法的模版
-
类定义了对象的结构和行为
2、类的组成
-
属性:指事物的特征,静态描述,例如:手机有品牌,价格,尺寸
-
行为:指事物所具有的功能,动态描述,例如:手机可以打电话,也可以
发短信。
3、类的理解
-
类是现实世界中具有相同事物的抽象集合。
-
类是对象的模版,对象是类的具体体现;或者,类是对象的抽象,对象是类的实例。
-
类是对象的数据类型,类是具有相同属性和行为的一组对象的集合。
-
简单理解:类就是对现实事物的一种描述。
4、类的声明
-
类的声明通常包括关键字class,后跟类名。
-
类名通常首字母大写,遵循驼峰命名规则。
-
类体包含属性(成员变量)和方法(成员方法)。
-
补充:驼峰命名法
-
如果只有一个单词全小写‘
例如:display() sum()
-
如果多个单词构成,则第一个单词全小写,后续单词首字母大写
例如:sayHello() getMax() findStudentById()
-
public class Person {
private String name; // 成员变量
private int age;
// 构造器
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 方法
public void sayHello() {
System.out.println("Hello, my name is " + name);
}
// 获取器(getter)
public String getName() {
return name;
}
// 设置器(setter)
public void setName(String name) {
this.name = name;
}
}
5、类的特性
-
成员变量:类中的变量,用于存储对象的状态
-
方法:类中的方法,用于定义对象的行为
-
构造器:特殊的方法,用于初始化新创建的对象
-
访问修饰符:public,protected,private,default(没有明确指定时,默认访问级别)
-
继承:类可以继承另一个类的属性和方法
-
多态:子类可以覆盖父类的方法,从而实现不同的行为
-
封装:隐藏对象内部的实现细节,只暴露必要的接口给外部使用
-
抽象类:可以包含抽象方法的类,不能被实例化
-
接口:定义了行为的标准,可以被类实现
6、类的使用
-
实例化:创建类的实例,即对象。
-
创建对象格式:
类名 对象名 = new 类名();
调用成员格式:
对象名.成员变量
对象名.成员方法() ;
示例:
public class Person { private String name; // 私有成员变量 private int age; // 默认构造器 public Person() { } // 参数化构造器 public Person(String name, int age) { this.name = name; this.age = age; } // 获取器(getter) public String getName() { return name; } // 设置器(setter) public void setName(String name) { this.name = name; } // 获取器(getter) public int getAge() { return age; } // 设置器(setter) public void setAge(int age) { this.age = age; } // 方法 public void sayHello() { System.out.println("Hello, my name is " + name); } // 重写 toString 方法 @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } } public class Main { public static void main(String[] args) { Person person = new Person("John Doe", 30); person.sayHello(); System.out.println(person); // 使用 setter 修改属性 person.setName("Jane Doe"); System.out.println(person.getName()); } } /* 在这个示例中,我们定义了一个 Person 类,它包含了私有成员变量 name 和 age,以及相应的 getter 和 setter 方法。此外,还有一个 sayHello 方法和重写的 toString 方法,用于打印对象的信息。 */
7、对象内存布局
-
栈内存:存放局部变量和对象的引用
-
堆内存:存放对象本身
-
方法区:存放类的元数据,如类名、方法名、常量池等
示例:
Person person = new Person("John Doe", 30);
-
解析:person是一个局部变量,存放在栈内存中
-
new Person("John Doe", 30)创建了一个新的Person对象,该对象存放在堆内存中。
-
person指向堆内存中的Person对象。
总结:
-
系统会为每个对象开辟单独的内存空间(堆空间),用来存储对象的属性。
-
类的成员方法存储在方法区,只保留一份,只要是该类的对象,都可以调用。
8、变量对比
1)静态变量:在类中声明且使用static关键字修饰的变量。
-
生命周期:随着类的加载而存在,随着类的加载而消失
-
存储位置:方法区
2)成员变量(实例变量)、局部变量的区别
-
定义位置不同
成员变量:类中方法外
局部变量:方法内部或方法声明上(形参列表)
-
内存中位置不同
成员变量:堆内存
局部变量:栈内存
-
生命周期不同
成员变量/实例变量:随着对象的存在而存在,随着对象的消失而消失。
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失。
-
初始化值不同
成员变量/实例变量:有默认初始化值
局部变量:没有默认初始化值,必须先定义、赋值才能使用
二、接口
Java中接口是引用数据类型的一种,它定义了一组方法的签名,而不提供具体的实现,接口在Java中扮演着非常重要的角色,特别是实现多态性和扩展性方面。
1、接口的概述
-
接口是对Java单继承的补充,接口是Java中一种重要的抽象机制,它提供了一种定义行为规范和实现多态性的方式。
-
接口最终也会被编译成 .class文件,但是一定要明确接口并不是类,而是另一种引用数据类型。
2、接口的定义
-
接口是一个完全抽象的类,它包含零个或多个抽象方法。
-
使用interface关键字定义接口。
-
接口中的方法不需要方法体,即不需要{};
-
接口中的方法默认是公共的(public)和抽象的(abstract),是抽象方法。
例如:public abstract void move(); 也可以简化为:void move();
-
接口中的数据成员默认是是公共的 (
public
)、静态的 (static
) 和最终的 (final
)。是常量,名称一般全大写。例如:public static final int NUM = 10;也可以简化为:int NUM = 10(等价)
public interface Drawable {
void draw(); // 抽象方法
int MAX_SIZE = 100; // 常量
}
3、接口的实现
-
类可以通过实现接口来提供接口中方法的具体实现。
-
一个类可以实现多个接口,这解决了Java中类只能单一继承的问题。
-
在实现接口时,类必须提供接口中所有抽象方法的具体实现,除非该类本身也声明为抽象类。
public class Circle implements Drawable {
@Override
public void draw() {
System.out.println("Drawing a circle.");
}
}
注意事项:
-
接口属于引用数据类型的一种,它不是类,它没有构造方法。
-
接口的实现类(子类),可以是正常类(重写所有抽象方法),也可以是抽象类(包含抽象方法)。
-
接口不能创建对象,一般用接口引用指向实现类对象。
-
在类和接口的实现关系中,可以使用多态,因为类和接口的实现关系,可以理解为继承的一种形式。
4、接口的继承
-
接口可以继承其它接口(多继承)。
-
接口继承使用extends关键字。
-
一个接口可以继承多个其他接口。
public interface Shape extends Drawable {
void resize(int percent);
}
5、接口的使用
-
通过接口引用指向实现接口的类的对象
-
接口支持多态性,可以使用接口类型的引用指向不同实现类的对象。
public class InterfaceDemo {
public static void main(String[] args) {
Drawable drawable = new Circle();
drawable.draw(); // 输出 "Drawing a circle."
Shape shape = new Circle();
shape.resize(50); // 假设 Circle 类实现了 resize 方法
}
}
注意事项:
-
接口多态应用时,编译看左边,运行看右边。
-
即接口引用只能调用接口中包含的方法,成功调用的是重写以后的方法。
JDK8及以后的变化
-
默认方法:从Java 8 开始,接口可以包含具有默认实现的方法,这些方法使用default关键字声明。
-
静态方法:从Java 8 开始,接口可以包含静态方法,这些方法使用static关键字声明。
-
增强了接口的功能
public interface Drawable {
void draw();
default void defaultMethod() {
System.out.println("This is a default method.");
}
static void staticMethod() {
System.out.println("This is a static method.");
}
}
综合完整实例:
public interface Drawable {
void draw(); // 抽象方法
int MAX_SIZE = 100; // 常量
default void defaultMethod() {
System.out.println("This is a default method.");
}
static void staticMethod() {
System.out.println("This is a static method.");
}
}
public class Circle implements Drawable {
@Override
public void draw() {
System.out.println("Drawing a circle.");
}
}
public class Square implements Drawable {
@Override
public void draw() {
System.out.println("Drawing a square.");
}
}
public class InterfaceDemo {
public static void main(String[] args) {
Drawable drawable = new Circle();
drawable.draw(); // 输出 "Drawing a circle."
drawable.defaultMethod(); // 输出 "This is a default method."
Drawable.staticMethod(); // 输出 "This is a static method."
drawable = new Square();
drawable.draw(); // 输出 "Drawing a square."
}
}
/*
在这个示例中,我们定义了一个 Drawable 接口,它包含了一个抽象方法 draw() 和一些其他特性。然后,我们定义了两个类 Circle 和 Square,它们实现了 Drawable 接口,并提供了 draw() 方法的具体实现。最后,在 main 方法中,我们展示了如何使用接口类型的引用指向不同的实现类的对象,并调用它们的方法。
*/
三、数组
1、数组概述
-
数组是一个容器,用于存储固定数量的同类型元素
-
数组中的每个元素可以通过索引访问,索引从0开始
-
数组的长度是固定的,一旦创建后不能改变
-
数组可以存储基本数据类型或引用数据类型
2、数组的定义
-
数组类型[] 数组名;(推荐使用)
int [] arr; char[] arr;
-
数组类型 数组名[];
int arr[]; double arr[];
-
注意:
数组是引用数据类型,用来存储一个引用值(可以理解为地址值)
数组没有进行初始化,不可以直接使用
3、数组初始化
定义数组(开辟栈空间内存)的同时,给其赋上初值,就叫做数组的初始化。
-
静态初始化:
数组的长度,取决于{}中元素列表的长度
数据类型[] 数组名 = new 数据类型 []{元素列表}
int[] numbers = new int[]{1, 2, 3, 4, 5}; // 初始化
-
特殊写法 (简化写法)不能把声明和赋值拆开写
数据类型[] 数组名 = {元素列表};
int[] numbers = {1, 2, 3, 4, 5}; // 直接初始化
-
动态初始化:
数据类型[] 数组名 = new 数据类型[数组长度];
注意事项:
-
数组中是有元素的。从物理意义上来讲
-
数组中的元素内容是不可能为空(整整意义上的无),任意时刻其实都是有值的。
-
当我们用动态初始化的方式创建数组时,数组中所有的元素位置都会被该数据类型的默认值填充
-
int[] arr1 = new int[3]; double[] arr2 = new double[2]; String[] arr3 = new String[4];
-
注意:
-
new 是一个关键字,表示为数组开辟内存空间
-
等号两边的数据类型要一致(先记住,后期会补充不一致的情况)
-
数组长度必须要有,可以>=0(一般大于0),但不能为负数
4、数组下标和长度
-
数组的下标的区间为[0, 数组长度 - 1]
-
如果数组的长度为length,那么数组下标的最小值为0,下标最大值为length-1
-
数组长度,是指在一个数组中,可以存放同一类型元素的最大数量
-
获取数组长度固定格式: 数组名.length
int[] arr = new int[4]; System.out.println(arr.length);
注意:
-
数组长度,必须在创建数组对象的时候就明确指定
-
数组长度,一旦确定,就无法改变
-
数组长度,可以>=0(一般大于0),但不能为负数
-
5、数组异常
-
索引越界:数组下标超出范围
程序运行后,将会抛出 ArrayIndexOutOfBoundsException 数组越界异
常。
解决方案:将错误的索引修改为正确的索引范围即可!
-
空指针:
arr = null;这行代码意味着变量arr将不再保存数组的内存地址,我们通过arr这个标识符再也找不到对空间数组异常,因此运行的时候会抛出NullPointerException空指针异常。
解决方案:给数组一个真正的堆内存空间引用即可。
6、二维数组
Java中的二维数组是一种特殊的一维数组,其中的每个元素本身也是一个一维数组。二维数组可以被视为表格或矩阵的形式,通常用于存储多维数据。下面详细介绍Java中二维数组的定义、初始化、访问和一些常见用法。
1)二位数组的概述
二位数组可以看作是一个数组的数组,其中每个元素又是一个一维数组。二维数组非常适合表示表格或矩阵形式的数据。
2)定义二维数组
-
数据类型 数组名[][];
-
数据类型[][] 数组名;
3)创建二维数组
-
数据类型 数组名 = new 数据类型行数[行数][列数];
-
数据型 数组名 = new 数据类型行数[行数][];
for (int i = 0; i < 行数; i++) { 数组名[i] = new 数据类型[列数]; }
4)初始化二维数组
数据类型[][] 数组名 = new 数据类型[行数][列数];
for (int i = 0; i < 行数; i++) {
for (int j = 0; j < 列数; j++) {
数组名[i][j] = 值;
}
}
5)访问二维数组元素
-
数组名行索引;
-
二维数组的遍历
public class Demo{
public static void main(String[] args){
//定义并创建二维数组
int[][] arr = new int[3][4];
//使用嵌套循环初始化二维数组
for(int i = 0;i < arr.length;i++){
for(int j = 0;j < arr.length; j++){
arr[i][j] = i * 10 + j;
}
}
//访问二维数组中的元素
System.out.println(arr[1][2]);
//遍历二维数组并打印所有元素
for(int i = 0;i < arr.length; i++){
for(int j = 0;j < arr.length; j++){
System.out.print(arr[i][j] + " ");
}
System.out.println();
}
}
}
6)二维数组的长度
-
获取行数:
int rows = 数组名.length;
-
获取列数:
int columns = 数组名[行索引].length;
下一节将更新冒泡排序、二分查找、选择排序、插入排序、希尔排序..