Spring Boot的主要目标是简化Spring应用程序的开发过程,通过提供默认的配置和自动化的配置方式,减少了繁琐的XML配置,并提供了一种约定优于配置的方式来快速开发应用程序。它还提供了一套强大且易于使用的命令行工具,可以用于创建、运行和管理Spring Boot应用程序。
Spring Boot具有以下特点:
简化配置:Spring Boot通过提供默认的配置和自动配置来简化应用程序的配置过程,开发者只需关注业务逻辑,无需手动配置底层的框架和组件。
内嵌服务器:Spring Boot内置了多个常用的Web服务器,如Tomcat、Jetty等,开发者无需手动配置,即可快速启动和运行Web应用程序。
自动装配:Spring Boot提供了自动装配机制,根据类路径中的依赖自动配置应用程序,简化了依赖包管理和配置文件的编写工作。
组件集成:Spring Boot集成了多个常用的组件和框架,如Spring Data、Spring Security、Spring MVC等,使开发者能够更方便地使用这些组件来构建应用程序。
健康监测:Spring Boot提供了健康监测的功能,可以通过HTTP端点实时监测应用程序的运行状态。
总之,Spring Boot通过简化配置、提供默认设置和自动化配置等方式,使得开发者能够更加快速、高效地构建和部署Spring应用程序。它的设计目标是提高开发效率、减少样板代码,并提供一种简单的方式来创建和管理Spring应用程序。
Spring:Spring是一个开源的Java应用程序开发框架。它提供了一种轻量级的解决方案,用于构建企业级Java应用程序。Spring提供了一个容器来管理应用程序中的对象,并提供了一系列的模块来处理不同的功能,如数据访问、事务管理、Web开发等。
Spring MVC:Spring MVC是Spring Framework的一个模块,用于构建Web应用程序。它采用了经典的MVC(Model-View-Controller)设计模式,将应用程序的逻辑分成三个部分:模型(Model),视图(View)和控制器(Controller)。Spring MVC提供了一套强大的API,用于处理Web请求、渲染视图、处理表单提交等。
Spring Boot:Spring Boot是一个用于构建独立的、基于Spring Framework的生产级别的应用程序的框架。它提供了一种简化的方式来创建和配置Spring应用程序,使开发者能够更快速、更轻松地构建Spring应用程序。Spring Boot通过提供默认配置和自动配置,减少了繁琐的XML配置,并提供了一种约定优于配置的方式来快速开发应用程序。
总结起来,Spring是整个框架,提供了基础的IoC(控制反转)和AOP(面向切面编程)功能;Spring MVC是Spring Framework的一个模块,用于构建Web应用程序,处理Web请求和渲染视图;Spring Boot是基于Spring Framework的一个框架,提供了一种简化的方式来创建和配置Spring应用程序,加速开发过程。
Iterable
接口是用来支持迭代器模式的关键接口,而Iterator
接口是Iterable
接口的一部分。Java中的集合类实现Iterable
接口的主要原因如下:
提供可迭代性:通过实现Iterable
接口,类可以告诉**编译器和其他开发者,它是一个可迭代的类,**可以使用增强的for循环来遍历其元素。这使得代码更加易读和简洁。
支持多次迭代:Iterable
接口要求类能够返回一个新的Iterator
对象,该对象可以用于多次迭代。这样可以方便地对集合进行多次遍历,而不需要重新创建集合的副本。
隐藏底层实现:Iterable
接口允许类隐藏其底层集合的实现细节。这样可以提高代码的封装性和安全性,同时也使得代码更加模块化和可维护。
总的来说,通过实现Iterable
接口,Java的集合类可以提供更好的可迭代性和灵活性,同时隐藏底层实现细节,使代码更加易读和模块化。这样可以提高代码的可维护性和可扩展性。
简介:
hashCode与equals这一对看似作用相同但细品却有着很大的区别,都是值比较而你我却有着不同的衡量标准。不如我们一起约定一个规则,来更好的为程序服务吧
有这么两个结论:
1、equals相等的两个对象他们的hashCode肯定相等,因此equals是绝对可靠的。
2、hashCode相等的两个对象他们的equals不一定相等,因此hashCode不是绝对可靠的。
什么是HashCode(哈希码)
hashCode()的作用就是获取哈希码,它是一个int整数。这个哈希码的作用是确定该对象在散列表中的索引位置。hashCode定义在java.Object中,意味着每个类都有hashCode函数。
public class Demo{
public static void main (String []args){
String str = “aa”;
System.out,println(str.hashCode())
}
}
3104
什么是equals
判断两个对象是否相等,就是“==”,两个对象的地址是否相同;如果对象重写了equals()的方法,则比较对象的内容是否相等。同样的equals定义在java.Object中,Java中任何类都有equals方法。
下图是不重写equals()的方法
那么由于两个对象的地址不同,所以得到的hashCode不同,即使他们的name一样,但是“==”与“equals()”都不相等,返回为false.
如果我们想要两个name相等的两个对象让它被系统认为是同一个,即调用equals()方法或者“==”时返回true,那么就需要重写equals方法了。而且在很多的情景下,我们判断两者是不是同一个name时,不需要判断其他信息,比如地址。怎么重写呢?
重写equals()方法还需要满足几个条件
自反性:对于任意的非空引用x, x.equals(x)返回为true。
对称性:对于任意引用x和y, x.equals(y)返回true,则y.equals(x)返回也应该为true。
传递性:对于任意引用x,y和z,如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也应该返回true。
一致性:如果x和y引用的对象没有发生变化,那么反复调用x.equals(y)得到的结果不变。
非空性:对于任意非空引用x, x.equals(null)的结果应该为false。
重写equals方法就必须重写hashCode方法,因为你返回true那么地址值也要相等这样的条件才能保证equals相等
线程安全性:
插入顺序:
底层实现:
允许键和值为null:
综上所述,Hashtable是线程安全的、不保证元素插入顺序的类;HashMap是非线程安全的、不保证元素插入顺序的类;LinkedHashMap是非线程安全的、保持元素插入顺序的类。根据具体的需求,选择合适的类使用。
Hashtable和HashMap是Java集合框架中用于存储键值对的类,它们的区别如下:
下面举例说明Hashtable和HashMap的区别:
import java.util.Hashtable;
import java.util.HashMap;
import java.util.Map;
public class HashTableVsHashMapExample {
public static void main(String[] args) {
// Hashtable例子
Hashtable<String, String> hashtable = new Hashtable<>();
hashtable.put("key1", "value1");
hashtable.put("key2", "value2");
System.out.println(hashtable.get("key1")); // 输出: value1
// HashMap例子
HashMap<String, String> hashMap = new HashMap<>();
hashMap.put("key1", "value1");
hashMap.put("key2", "value2");
System.out.println(hashMap.get("key1")); // 输出: value1
// 多线程环境下的Hashtable例子
Runnable runnable1 = new Runnable() {
@Override
public void run() {
System.out.println(hashtable.get("key1")); // 输出: value1
}
};
Runnable runnable2 = new Runnable() {
@Override
public void run() {
System.out.println(hashtable.get("key2")); // 输出: value2
}
};
Thread thread1 = new Thread(runnable1);
Thread thread2 = new Thread(runnable2);
thread1.start();
thread2.start();
// 多线程环境下的HashMap例子
Runnable runnable3 = new Runnable() {
@Override
public void run() {
System.out.println(hashMap.get("key1")); // 输出: value1 或者 null
}
};
Runnable runnable4 = new Runnable() {
@Override
public void run() {
System.out.println(hashMap.get("key2")); // 输出: value2 或者 null
}
};
Thread thread3 = new Thread(runnable3);
Thread thread4 = new Thread(runnable4);
thread3.start();
thread4.start();
}
}
在上述例子中,Hashtable和HashMap都能够成功存储键值对,并且通过键获取值。然而,在多线程环境下,**Hashtable能够保证数据的一致性,两个线程同时访问Hashtable实例时,**得到的值都是正确的。而HashMap则可能出现数据不一致的情况,两个线程同时访问HashMap实例时,有可能得到的值是正确的,也有可能是null,这取决于线程之间的竞争情况。
因此,在多线程环境下,如果需要保证数据一致性,可以选择使用Hashtable;如果不需要考虑线程安全性,可以选择使用HashMap。
基本数据类型是Java语言中的原始数据类型,包括boolean、byte、short、int、long、float、double和char。这些基本数据类型是直接存储值的,它们不是对象,没有方法和属性。
Object是Java中的根类,是所有类的直接或间接父类。它是一个通用的引用类型,可以引用任何类型的对象,包括基本数据类型的包装类。
基本数据类型的包装类是由Java为每个基本数据类型提供的对应类,用于将基本数据类型转换为对象以便操作。包装类包括Boolean、Byte、Short、Integer、Long、Float、Double和Character。
基本数据类型的包装类都继承自Object类,因此它们都可以被当作Object来使用。这意味着可以将基本数据类型的包装类作为方法的参数或返回值,也可以将其存储在集合类中。
在需要将基本数据类型作为对象处理的时候,可以使用基本数据类型的包装类来封装成对象。这样就可以使用一些Object类提供的方法,比如toString()、equals()等。
下面是一个示例代码,展示了基本数据类型和Object之间的关系:
public class BasicTypeAndObjectExample {
public static void main(String[] args) {
// 基本数据类型
int num = 10;
// 基本数据类型的包装类
Integer numObject = new Integer(num);
// 基本数据类型的包装类作为Object使用
Object obj = numObject;
System.out.println(obj.toString()); // 输出: 10
System.out.println(obj.equals(new Integer(10))); // 输出: true
// 将基本数据类型封装成对象
Integer numObject2 = Integer.valueOf(20);
// 基本数据类型的包装类作为方法参数
printNumber(numObject2);
}
public static void printNumber(Object obj) {
System.out.println(obj.toString());
}
}
在上述示例中,首先使用基本数据类型int来存储一个整数,并通过Integer类将其封装为对象。然后,将该对象赋值给Object类型的变量,通过Object类提供的方法来操作该对象。最后,将基本数据类型的包装类作为方法的参数传递给printNumber()方法,并在方法内部将其当作Object来使用。
通过包装类,我们可以将基本数据类型转换为对象,从而可以使用Object类的方法和特性。这种转换在某些需要对象操作的场景中非常有用。
基本数据类型byte本身是一个基本数据类型,不是对象,因此没有getClass()方法。getClass()方法是Object类定义的方法,用于返回一个对象的运行时类。只有引用类型(即对象)才可以调用该方法,基本数据类型没有这个方法可供调用。
然而,byte有对应的包装类Byte,它是byte的包装类。Byte类是Object类的子类,因此可以调用getClass()方法。该方法返回一个Class对象,表示包装类的运行时类。
以下是一个示例代码,展示了基本数据类型byte和包装类Byte的getClass()方法的不同使用方式:
public class ByteExample {
public static void main(String[] args) {
byte num = 10;
Byte byteObj = new Byte(num);
// 基本数据类型调用getClass()方法,会编译错误
// byte类没有getClass()方法
// System.out.println(num.getClass());
// 包装类调用getClass()方法,返回运行时类
System.out.println(byteObj.getClass()); // 输出: class java.lang.Byte
}
}
在上述示例中,首先定义了一个基本数据类型byte并初始化为10。由于基本数据类型byte没有getClass()方法,所以在尝试调用num.getClass()时会导致编译错误。然后,创建了一个Byte对象byteObj,通过byteObj.getClass()调用getClass()方法,返回一个Class对象,表示包装类Byte的运行时类。最后,将结果打印出来,输出为"class java.lang.Byte"。
在Java中,堆(Heap)和栈(Stack)是两种常用的内存管理方式。它们用于存储不同类型的数据或者对象,并且在内存中的分配和回收方式也有所不同。
堆(Heap):
栈(Stack):
下面是一个简单的示例,用来说明堆和栈的使用方式和区别:
public class StackHeapExample {
public static void main(String[] args) {
// 栈中分配的内存
int a = 10;
int b = 20;
int c = a + b;
// 堆中分配的对象
String str1 = new String("Hello");
String str2 = new String("World");
String str3 = str1 + str2;
System.out.println(c); // 输出: 30
System.out.println(str3); // 输出: HelloWorld
}
}
在上述示例中,首先在栈中分配了三个整型变量a、b和c,并对其进行运算。在栈中,这些变量的内存会被自动分配和释放。然后,在堆中分配了三个字符串对象str1、str2和str3,并对其进行字符串拼接操作。在堆中,这些对象的内存需要手动进行释放。
总结起来,堆和栈的区别在于存储的内容、分配方式和内存管理方式不同。堆主要用于存储对象实例,需要手动进行内存管理;栈主要用于存储局部变量和方法调用,内存管理由编译器自动处理。#
Java反射是指在程序运行过程中,通过反射机制动态获取、操作和修改类的属性、方法和构造函数等信息的能力。它使得程序能够在运行时动态地调用类的方法、访问和修改类的属性,甚至可以在运行时创建对象和调用私有方法。
Java反射主要通过以下几个类和接口来实现:
下面是一个简单的示例,用来说明Java反射的使用方式:
public class ReflectionExample {
private String message;
public ReflectionExample() {
message = "Hello, Reflection!";
}
public void printMessage() {
System.out.println(message);
}
private void privateMethod() {
System.out.println("This is a private method.");
}
public static void main(String[] args) throws Exception {
// 获取类的Class对象
Class<?> clazz = ReflectionExample.class;
// 创建对象
Object obj = clazz.getDeclaredConstructor().newInstance();
// 调用方法
Method printMessageMethod = clazz.getMethod("printMessage");
printMessageMethod.invoke(obj);
// 访问私有属性
Field messageField = clazz.getDeclaredField("message");
messageField.setAccessible(true);
String messageValue = (String) messageField.get(obj);
System.out.println(messageValue);
// 调用私有方法
Method privateMethod = clazz.getDeclaredMethod("privateMethod");
privateMethod.setAccessible(true);
privateMethod.invoke(obj);
}
}
在上述示例中,首先通过Class.forName()方法或者直接使用类名获取了ReflectionExample类的Class对象。然后,通过Class对象可以获取类的构造函数(getDeclaredConstructor())、方法(getMethod())和属性(getDeclaredField())。接着,通过newInstance()方法创建了ReflectionExample对象,并使用getMethod()方法获取printMessage方法的Method对象,然后通过invoke()方法调用了该方法。再通过getDeclaredField()方法获取了message属性的Field对象,并使用setAccessible()方法设置为可访问,然后使用get()方法获取属性的值。最后,通过getDeclaredMethod()方法获取了privateMethod方法的Method对象,并同样设置为可访问,然后使用invoke()方法调用了该方法。
这个示例展示了Java反射的基本用法,通过反射可以在运行时动态地获取、操作和修改类的属性、方法和构造函数等信息,增加了程序的灵活性和可扩展性。但是,
过度使用反射可能会导致性能下降和安全问题,因此在使用时需要谨慎考虑。
反射机制是 Java 语言的一个重要特性。在学习 Java 反射机制前,大家应该先了解两个概念,编译期和运行期。
编译期是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成 class 文件的过程。
编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误。
运行期是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。
Java 反射机制是在运行状态中,
对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为
Java 语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。在 Java
中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。Java 反射机制在服务器程序和中间件程序中得到了广泛运用。在服务器端,往往需要根据客户的请求,动态调用某一个对象的特定方法。此外,在
ORM 中间件的实现中,运用 Java 反射机制可以读取任意一个 JavaBean 的所有属性,或者给这些属性赋值。
这句话是正确的。在Java中,
Object类中的equals方法用于比较两个对象是否相等,**而hashCode方法用于计算对象的哈希码值。**根据Java规范,如果两个对象使用equals方法比较返回true,则它们的hashCode值应该相等。但是,反之则不一定成立,即两个对象的hashCode值相等并不意味着它们一定相等。
这是因为hashCode方法是根据对象的内部状态计算出来的,而equals方法是根据业务逻辑来判断对象是否相等。即使两个对象的值(内部状态)相同,但它们的hashCode值可能是根据不同的计算方式得出的,因此可能不同。
为了遵循Java规范,当重写equals方法时,必须同时重写hashCode方法,以确保对象的相等性和哈希码的一致性。
在Java中,当一个对象被作为参数传递给一个方法时,
实际上是将对象的引用(内存地址)传递给了方法,而不是对象本身。因此,这里可以说是引用传递。
在方法中,可以通过引用来访问和修改对象的属性,因为方法中使用的是对象的引用,指向的是同一个对象。所以,如果方法修改了对象的属性,这些修改会在方法执行完后仍然保持。
然而,需要注意的是,当方法返回一个结果时
,实际上返回的是一个新的对象或基本数据类型的拷贝,而不是原始对象本身。所以,返回结果不会影响原始对象的引用。
综上所述,虽然对象通过引用传递给方法,方法可以修改对象的属性,但方法返回的结果不会影响原始对象本身。因此,在这种情况下,可以认为是值传递。
在Java中,char类型是用来表示单个字符的数据类型,它使用16位Unicode编码来表示字符。Unicode编码可以表示世界上几乎所有的字符,包括中文汉字。
因此,char类型的变量是可以存储一个中文汉字的。例如,可以使用以下方式来声明和初始化一个char类型的变量来存储一个中文汉字:
char chineseChar = '中';
在上面的例子中,chineseChar变量存储了一个中文汉字’中’,这是完全合法的。
需要注意的是
,由于Java使用的是UTF-16编码,在某些特殊情况下,一个字符可能需要占用两个char类型的变量。例如
,一些特殊的中文字符或表情符号可能需要两个char类型的变量来表示。
综上所述,char类型的变量完全可以存储一个中文汉字,只需要注意特殊情况下可能需要使用多个char类型的变量来表示一个字符。
instanceof关键字是Java中的一个运算符,用于判断一个对象是否是某个类的实例或者实现了某个接口。
其作用如下:
判断对象是否是某个类的实例:可以使用instanceof关键字来判断一个对象是否属于某个类的实例。例如,可以使用obj instanceof MyClass
来判断obj对象是否是MyClass类的实例。
判断对象是否实现了某个接口:可以使用instanceof关键字来判断一个对象是否实现了某个接口。例如,可以使用obj instanceof MyInterface
来判断obj对象是否实现了MyInterface接口。
多态性中的类型转换:instanceof关键字可以在多态性中进行类型转换的判断。在进行类型转换之前,可以使用instanceof关键字来判断对象是否是某个类的实例,以避免出现ClassCastException异常。
示例代码如下:
class Animal {
}
class Dog extends Animal {
}
class Cat extends Animal {
}
public class Example {
public static void main(String[] args) {
Animal animal = new Dog();
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark();
} else if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.meow();
}
}
}
在上述示例中,animal对象被实例化为一个Dog对象,并使用instanceof关键字判断animal对象是否是Dog类的实例。根据判断结果的不同,可以进行相应的类型转换和方法调用。
a = a + b 和 a += b 在功能上是相同的,都是将变量b的值加到变量a上,并将结果赋值给变量a。
然而,它们在底层实现和使用时有一些细微的差别:
它的结果是一个新创建的对象。而 a += b 是一个复合赋值表达式,会直接在原有的对象上进行修改。
对象引用的不同:在 a = a + b 中,右边的表达式会创建一个新的对象,然后将新的对象的引用赋给变量a。而在 a += b 中,不会创建新的对象,直接在原有的对象上进行修改。
适用类型的不同:a += b 只能用于某些特定的基本数据类型(如int、long等)和一些特定的类(如String),而 a = a + b 可以适用于更多的数据类型。
可读性和代码简洁性:a += b 相对于 a = a + b 更加简洁和易读,可以减少代码量。
需要注意的是,虽然 a += b 的实现可能在底层上效率更高,但在大多数情况下,这两种写法的性能差异并不明显,因此选择哪种写法主要取决于个人的编码风格和偏好。
Exception 和 Error 是 Java 中的两个不同的异常类层次结构。
Exception 类下还有两个子类:RuntimeException 和 IOException。
Error 类下有一些子类,如 OutOfMemoryError、StackOverflowError 等,这些错误通常是由于系统资源耗尽或者 JVM 内部错误引起的,无法通过代码进行处理和修复。
总结:
Exception 和 Error 都是继承自 Throwable 类,表示在程序执行过程中的异常情况,但它们在处理方式和出现场景上有所不同。
Object类是Java中所有类的父类,它定义了一些常用的方法,下面对其中几个常用方法进行简要说明:
equals(Object obj):用于判断当前对象是否与给定的对象相等。默认情况下,该方法使用的是引用比较,即只有两个对象的引用指向同一个内存地址时才认为它们相等。如果子类希望根据实际需求重写该方法,可以修改比较的逻辑。
**hashCode():**返回当前对象的哈希码值。哈希码是一个整型值,用于快速查找对象在哈希表中的位置。通常情况下,如果两个对象通过equals()方法比较相等,它们的哈希码应该相等。
toString():返回当前对象的字符串表示。默认情况下,该方法返回类名和对象的哈希码。可以根据实际需求重写该方法,使其返回更有意义的信息。
getClass():返回当前对象的运行时类对象。该方法可以用来获取对象所属的类,以便
常用的Java并发工具类包括以下几个:
CountDownLatch:用于控制一个或多个线程等待其他线程完成操作后再继续执行。它通过一个计数器来实现,初始化时设置计数器的值,每当一个线程完成操作时,计数器的值减一。当计数器的值变为0时,所有等待的线程将被释放。
CyclicBarrier:与CountDownLatch类似,也是用于控制线程的执行顺序。不同之处在于,CyclicBarrier设置了一个屏障,当指定数量的线程都到达屏障时,所有线程才能继续执行。与CountDownLatch不同,CyclicBarrier的计数器可以重置,可以用于循环执行的场景。
Semaphore:
用于控制同一时间并发访问的线程数量。它通过设置许可数量来限制同时访问的线程数量。每当一个线程访问结束后,释放一个许可,使其他等待的线程可以继续执行。
用于两个线程之间交换数据。每个线程通过调用exchanger方法交换数据,当两个线程都调用了exchanger方法后,它们可以获取彼此交换的数据。
Phaser:类似于CyclicBarrier,也是用于控制线程的协同执行。不同之处在于,Phaser可以动态地注册和注销参与者,并根据参与者的数量来决定是否继续执行。它可以用于分阶段执行任务的场景。
Lock和Condition:Lock是一个可重入的互斥锁,
它比synchronized关键字更加灵活,可以手动控制锁的获取和释放。Condition是与Lock配合使用的条件变量,可以用于线程间的等待和通知机制。
这些并发工具类在多线程编程中起到了重要的作用,能够提高线程的效率和安全性。在具体
通过反射调用对象的方法可以使用Java中的java.lang.reflect包中的相关类和方法。下面是通过反射调用对象方法的简要步骤:
获取要调用方法的对象的Class对象。可以通过对象的getClass()方法或者使用Class.forName()方法获取。
使用Class对象的getMethod()方法获取要调用的方法。
需要传入方法的名称和参数类型
- 使用Method对象的invoke()方法调用方法。需要传入要调用方法的对象实例和方法的参数(如果有参数的话)。
下面是一个示例代码,演示了如何通过反射调用对象的方法:
import java.lang.reflect.Method;
public class ReflectExample {
public static void main(String[] args) throws Exception {
// 创建一个字符串对象
String str = "Hello, World!";
// 获取String类的Class对象
Class<?> cls = str.getClass();
// 获取String的substring方法
Method method = cls.getMethod("substring", int.class, int.class);
// 调用substring方法
String result = (String) method.invoke(str, 0, 5);
// 输出结果
System.out.println(result); // 输出:Hello
}
}
在上述示例中**,首先获取了字符串对象的Class对象,然后使用getMethod()方法获取了substring方法的Method对象。最后使用invoke()方法调用了substring方法,并传入了参数0和5,得到了调用结果**。
bit是计算机中最小的数据单位,表示二进制位,取值为0或1。它是计算机存储和处理信息的基础。
byte是计算机中常用的数据存储单位,由8个bit组成,可以表示256个不同的值(从0到255)。在计算机中,通常使用byte来表示字符、整数和其他数据类型。
字符(char)是Java中的数据类型,用于表示单个字符。它是16位的无符号整数类型,可以用来存储Unicode字符。字符类型的长度是2个字节。
区别如下: