1.基本语法类型
数据类型 | 关键字 | 内存占用 | 范围 |
---|---|---|---|
字节型 | byte | 1 | -128 ~ 127 |
短整型 | short | 2 | -32768 ~ 32767 |
整型 | int | 4 | -2^31 至 2^31 - 1 |
长整型 | long | 8 | -2^63 至 2^63 - 1 |
单度精度浮点数 | flaot | 4 | 无需关注 |
双精度浮点数 | double | 8 | 无需关注 |
字符型 | char | 2 | 0 ~ 65535 |
布尔型 | boolean | 没有明确规定 | true或false |
Java提供了上述的基本数据类型和其他编程语言如c/c++类似,同时Java还提供了以上基本数据的包装类。包装类就是一个类,学过其他面向对象的语言对类都不陌生。包装类相当于对基本数据类型封装一层,提供了一些基本数据的没有的能力。这些包装类允许你将基本类型当作对象来处理,从而可以利用Java的面向对象特性,如继承、多态等。基本数据类型和对应的包装类可以自由的转换,在Java中被称为装箱和拆箱。
基本数据类型 | 包装类 |
---|---|
byte |
Byte |
byte |
Byte |
short |
Short |
int |
Integer |
long |
Long |
float |
Float |
包装类有如下好处:
封装:包装类允许你将基本类型的数据封装成对象,这有助于更好地组织和管理数据。
方法调用:包装类提供了许多实用的方法,如数值转换、字符串转换等。
序列化:对象可以被序列化,而基本类型不能直接序列化。
使用集合框架:Java集合框架(如
List
,Set
,Map
等)要求存储的是对象而不是基本类型,因此需要使用包装类。空值支持:包装类支持
null
值,而基本类型总是有默认值(如int
的默认值为0
)。
public class WrapperExample {
public static void main(String[] args) {
// 创建包装类实例 // 创建包装类实例
Integer myInt = new Integer(10);
// 自动装箱 用整型构造包装类对象
Integer autoBoxedInt = 20;
// 将包装类转换为基本类型
int basicInt = myInt.intValue();
// 自动拆箱
int autoUnboxedInt = autoBoxedInt;
// 使用包装类的方法 将字符串装转成整型
Integer parsedInt = Integer.parseInt("123");
// 打印结果
System.out.println("Wrapped Int: " + myInt);
System.out.println("Auto-boxed Int: " + autoBoxedInt);
System.out.println("Unwrapped Int: " + basicInt);
System.out.println("Auto-unboxed Int: " + autoUnboxedInt);
System.out.println("Parsed Int: " + parsedInt);
}
}
命名规范
Java中合法的变量名和C/C++一样,但是有个约定俗成的规则就是类名是大驼峰,变量名和方法名是小驼峰
2.引用类型
Java中除了基本数据类型外就是引用类型,数组和String以及自定义类型都是属于引用类型。Java强调一切皆对象,在构建引用变量的时候一般用new关键字去创建对象。其实这点和c++也是类似的,本质都是在堆空间上申请资源,但是Java有gc机制不必像c++那样手动释放资源,JVM(Java虚拟机)会进行内存管理。
Java内存管理如下:
Java内存通常可以划分为以下几个部分:
- 栈(Stack)
- 堆(Heap)
- 方法区(Method Area)
- 程序计数器(Program Counter Register)
- 本地方法栈(Native Method Stack)
1. 栈(Stack)
栈存储了线程中的局部变量表、操作数栈、动态链接、方法出口等信息。每一个线程都有自己的栈,每当一个新线程启动时,JVM就会为这个线程创建一个新的栈。栈中的内存分配是线程私有的,随方法的执行而生,随方法的退出而消亡。
2. 堆(Heap)
堆是Java虚拟机中最大的一块内存区域,被所有线程共享,用于存放对象实例,几乎所有的对象实例都在这里分配内存。这是垃圾收集器管理的主要区域,也是大多数对象所在的地方。当对象不再被引用时,垃圾收集器就会回收这部分内存。
3. 方法区(Method Area)
方法区存放了每个类的信息(包括类名、方法信息、常量、静态变量等)、静态变量、常量池等内容。它是被各个线程共享的内存区域,类似于Java堆,但目的不是存放实例而是存放类的信息。
4. 程序计数器(Program Counter Register)
每个线程都有一个独立的程序计数器,用于指示当前线程正在执行的字节码指令的位置。它是唯一一个不会发生OutOfMemoryError的区域,因为它的大小在类加载的时候就已经确定了。
5. 本地方法栈(Native Method Stack)
与虚拟机栈所发挥的作用非常相似,区别在于:虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则是为虚拟机使用到的Native方法服务。(Native方法是Java底层用到的C/C++方法)
String类型本质也是一个类,它是专门用来存储字符串的,和C++的STL中的string不同,String在Java中是一个对象,不支持下表随机访问,但是可以通过String提供的方法进行字符的遍历
public class Test {
public static void main(String[] args) {
String str = "hello";
for(int i=0;i<str.length();i++){
//获取字符串中的每个字符
char ch = str.charAt(i);
System.out.println(ch);
}
}
}
以上使用String提供的charAt方法获取每个字符进行遍历,在创建对象的时候都要通过new关键字,上述的代码中看似是将字符串直接赋值给str,但是本质上还是产生了new的动作这种写法像当于简写,数组类型也可以这样简写。
public class Test {
public static void main(String[] args) {
int [] arr1 = new int[4];
int [] arr2 ={
1,2,3,4};
int [] arr3 = new int[] {
1,2,3,4};
for(int i=0;i<arr1.length;i++){
System.out.println(arr1[i]);
}
System.out.println("==========");
for(int i=0;i<arr2.length;i++){
System.out.println(arr2[i]);
}
System.out.println("==========");
for(int i=0;i<arr3.length;i++){
System.out.println(arr3[i]);
}
}
}
以上就是数组的几种创建方式,第一种方式在创建数组对象的时候指明了数组的空间大小,因为没有添加任何数据,打印结果是数据类型的默认值,整型的默认值就是0
关于引用
基本数据类型创建的变量,称为基本变量,该变量空间中直接存放的是其所对应的值; 而引用数据类型创建的变量,一般称为对象的引用,其空间中存储的是对象所在空间的地址。所谓的 “引用” 本质上只是存了一个地址. Java 将数组设定成引用类型, 这样的话后续进行数组参数传参, 其实 只是将数组的地址传入到函数形参中. 这样可以避免对整个数组的拷贝(数组可能比较长, 那么拷贝开销就会很大).
如图所示,在main方法中定义的一个arr变量是整形数组的引用,arr在栈上的地址是0x123,它所存储的值是0x456也就是堆上数组的地址,学过c/c++对引用的理解应该很快不是很难
关于数组
Java中的数组和c/c++的数组也是类似,在一片连续的空间中存储数据。Java也有二维数组在理解上和c/c++类似,本质也是一维数组,不过数组中的每个元素相当于是一维数组,Java和c/c++不同的是Java中可以省略列号但是不可以省略行号。
public class Test {
public static void main(String[] args) {
int [][] arr =new int[3][];
arr[0] = new int [2];
arr[1] = new int [1];
arr[2] = new int [3];
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
if((i==0&&j==2)||(i==1&&j==1)){
break;
}
arr[i][j]=i+j;
}
}
for(int[] a:arr){
for(int e: a ){
System.out.print(" "+e);
}
System.out.println();
}
}
}
Java中的每个行存储的数据个数可以不同这叫做不规则数组,同时Java语法也支持C++11中的范围for去遍历数组元素
3.输入输入和循环控制运算符
在Java中通过创建Scanne类型的对象获取键盘输入的数据,在使用Scanne对象的时候需要通过import关键字导包,有点类似于C/C++中导入头文件。在Java中包本质上就是一个目录,导包就是导入某个目录下的某个Java文件中的某个类型,让编译器能够认识导入的符号。
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int age = scanner.nextInt();
String name = scanner.nextLine();
double score = scanner.nextDouble();
System.out.println(" "+age+" "+name+" "+score);
}
}
上述代码中通过Scanner提供的方式获取对应的整型,字符串,浮点看类数据,当然Scanner方法不止这么一点,还有一些其他的方法。在使用Sacnner类型的时候在new对象时需使用system中的in字段来构造对象。这是单行数据输入,还有对应的多行数据输入,如下图代码所示。*
public class Test {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
while (in.hasNextLine()) {
int a = in.nextInt();
int b = in.nextInt();
System.out.println(a + b);
}
}
}
多行输入的时候,通过hasNextLine方法来判断键盘中是否还有要输入的数据,通常在我们刷题的时候需要这种方式来读取OJ中的多行测试用例。关于输出我们使用System.out提供的打印方法进行数据输出。
关于导包
导包除了直接指定外,还有静态导包和通配符导包,如下方代码所示。
import static java.lang.System.out;
public class MyClass {
public static void main(String[] args) {
out.println("Hello, World!");
// 直接使用 System 类中的静态方法 println()
}
}
import java.util.*; // 导入 java.util 包下的所有类
public class MyClass {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
}
}
循环控制和运算符
循环控制和运算符和其他编程语言也是类似的比如C/C++,循环就是for while switch 语句,运算符就是加减乘除取模和左移右移等操作;不同类型的数据之间相互运算时,数据类型小的会被提升到数据类型大的
int a = 10;
long b = 20;
int c = a + b; // 编译出错: a + b==》int + long--> long + long 赋值给int时会丢失数据
long d = a + b; // 编译成功:a + b==>int + long--->long + long 赋值给long
//byte与byte的运算
byte a = 10;
byte b = 20;
byte c = a + b;
上述代码中byte 和 byte 都是相同类型, 但是出现编译报错. 原因是, 虽然 a 和 b 都是 byte, 但是计算 a + b 会先将 a
和 b 都提升成 int, 再进行计算, 得到的结果也是 int, 这是赋给 c, 就会出现上述错误.
由于计算机的 CPU 通常是按照 4 个字节为单位从内存中读写数据. 为了硬件上实现方便, 诸如 byte 和 short
这种低于 4 个字节的类型, 会先提升成 int, 再参与计算.
4.方法的使用
Java中方法也就是C/C++中的函数,Java中的方法也支持函数重载,不过和C/C++不同的是Java中的方法需要定义在类中,而且每个类中都可以定义一个main方法,main方法的主体写法是固定的,如下图所示。
public class Test {
public static void main(String[] args) {
}
}
main函数被static修饰是属于是静态方法,返回值是viod,参数是String类型用来接受输入的不定参数,学过c/c++玩过Liunx的很容易理解这个参数就是来接收在命令行中输入的一些参数选项,不过在Java中基本上用不到这个参数。在Java中方法不仅支持重载也支持递归语法。
public class Test { public static void func(char a){ System.out.println("func_char_a"); } public static void func(double a){ System.out.println("func_double_a"); } public static int func(int n){ if(n<=2){ return n; } return func(n-1)+func(n-2); } public static void main(String[] args) { System.out.println(func(5)); func(1.2); func('a'); } }
和c/c++一样如果方法递归层数太多也会栈溢出,被static修饰的方法属于是静态方法,Java类中也有this的概念,被static修饰的方法是属于类的,该方法中也没有this。
5.类和对象
Java和c++一样都是面向对象的编程语言,在Java中使用class关键字来定义类,类是对对象的抽象,在类中中包含对象的属性和方法。类的实例化就是用类去创建一个对象的过程,Java 中使用 new 关键字配合类名来实例化对象。
class Students{
private int age;
private String name;
public Students(String name,int age){
this.age=age;
this.name=name;
}
@Override
public String toString() {
return "Students{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
public class Test {
public static void main(String[] args) {
Students s = new Students("lisi",10);
System.out.println(s)