任务描述
本关任务:将键盘输入的大写字母转化为小写字母。
相关知识
为了完成本关任务,你需要掌握:
- 字符型变量和常量;
- 字符型数据的加减运算;
- 字符型数据的输入/输出。
字符型变量和常量
在之前我们学习了整型和浮点型的变量和常量,接下来介绍字符型的变量和常量。
首先我们要先了解一下什么是字符,我们之前学过 Unicode 字符集,这里的字符值的就是 Unicode 字符集中的字符。
字符型常量
字符常量,用单引号引用起来的的单个字符,就叫字符常量,例如:'w','2','R' 等。
例子:
-
public class Demo {
-
`public static void main(String[] args) {`
-
`// 单引号里面是字符常量,里面的内容不可以为空,必须是一个字符,不可以是两个或多个。`
-
`System.out.println('q');`
-
`System.out.println('汉');`
-
`}`
-
}
字符型变量
Java 中的字符型变量类型为 char ,它是一个单一的两个字节的 16 位 Unicode 字符;最小值是 \u0000(即为 0 );最大值是 \uffff(即为 65535 );默认值为空;用单引号引起来的部分;它可以储存任何字符,示例如下:
// 声明一个变量名为 a,数据类型为 char 的变量,并赋予该变量的值为 A
char a = 'A';
// 声明一个变量名为 b,数据类型为 byte 的变量,并赋予该变量的值为 汉
char b = '汉';
// 输出 a,b 两个变量
System.out.println(a);
System.out.print(b);
执行结果如下:
A
汉
字符运算
字符类型是可以运算的,因为字符在 ASCII 等字符编码表中有对应的数值。在 Java 中,对字符型字符运行时,直接当做 ASCII 表对应的整数来对待。
示例:
// 定义一个字符型变量a,值为 a
char a = 'a';
// 定义一个字符型变量b,值为 b
char b = 'b';
// 定义一个字符型变量c,值为 中
char c = '中';
// 定义一个字符型变量d,值为 国
char d = '国';
// 字符之间运算,类型会提升为 int 类型
int m = a + b; // 计算 a+b 的值
System.out.println(a);
int n = a - b; // 计算 a-b 的值
System.out.println(b);
System.out.print(c+d);
执行结果:
195
-1
42282
我们发现字符之间运算,类型会提升为 int 类型,而如果我们并不想输出它的 ASCII 码的值,而是想要输出它对应的字符该怎么办呢?
我们可以将 int 类型强制转换为 char,当我们想要将一个大范围的数据类型转换为小范围的数据类型,我们需要进行强制转换。在后面的学习任务中,我们会为大家详细介绍强制转换。
// 定义一个字符型变量a,值为 a;
char a = 'a';
// 将 int 类型强制转化为 char 型。
char m = (char)(a - 32);
// 定义一个字符型变量c,值为 中;
char c = '中';
// 定义一个字符型变量d,值为 国;
char d = '国';
// 将 int 类型强制转化为 char 型。
char n = (char)(c+d);
System.out.println(m);
System.out.print(n);
执行结果:
A
ꔪ
我们可以看到 a-32 结果为 A,这是为什么呢?
因为字符 char 采用的是 Unicode 编码的 16 位字符类型,其表示范围是 0-65536。标准的 8 位 ASCII 字符集是 Unicode 的子集,其取值范围为 0-127。大小写字母之间正好相差 32。
当然,我们还要注意的是字符串常量之间的加减运算并不需要强制转换,但是我们要注意,当它结果超出 char 的范围后会溢出。
char m = 'a' - 32;
System.out.print(m);
执行结果:
A
字符运算我们已经有了一定的了解,那字符的输入输出又是怎样的呢?
字符输入输出
在之前我们学习了整型和浮点型的输入,那我们接着来看字符型的输入输出。
它与整型和浮点型都是通过实例一个 Scanner 对象来使用,字符型是调用reader.next().charAt(0)
方法来获取键盘输入的字符。charAt(0)
括号中的参数可以不为 0 ,在后期的学习任务中我们会为大家详细介绍。
-
import java.util.Scanner;
-
public class Demo{
-
`public static void main(String[] args) {`
-
`// 第一步:创建一个Scanner的实例对象,命名为reader`
-
`Scanner reader = new Scanner(System.in);`
-
`// 从键盘处接收用户输入的字符型数据`
-
`char a= reader.next().charAt(0);`
-
`System.out.print(a);`
-
`}`
-
}
用户键盘输入:a
执行结果:
a
这就是如何获取键盘输入字符型数据的操作了,那我们接下来学习如何输出字符型数据。
我们之前学到的System.out.print()
和System.out.println()
,对于输出字符型数据与输出整型与字符型都是一样的。
例子:
char a = '中';
char b = '国';
System.out.println(a)
System.out.print(b);
执行结果:
中
国
接下来我们来看格式化输出字符型数据。输出整型用的符号是 %d,而浮点型是 %f,那字符型是什么呢?
%c 是格式化输出字符型符号,来看一个例子。
char d = '汉'
System.out.printf("我是%c族人",d);
执行结果:
我是汉族人
编程要求
仔细阅读右侧编辑区内给出的代码框架及注释,按照提示编写程序代码。
测试说明
平台将使用测试集运行你编写的程序代码,若全部的运行结果正确,则通关。 可在右侧“测试结果”区查看具体的测试集详情。
编写代码
/**
* 任务:将键盘输入的字符转化为小写字母。
* 类名为:LowerCase
*/
import java.util.Scanner;
public class LowerCase {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
/********** Begin **********/
// 第一步:获取键盘输入的值
char uppercaseChar = reader.next().charAt(0);
// 第二步:将获取到的大写字母转化为小写字母
char lowercaseChar = Character.toLowerCase(uppercaseChar);
// 第三步:不换行输出转化后的小写字母
System.out.print(lowercaseChar);
/********** End **********/
reader.close();
}
}
代码分析
当初学习编程时,理解代码中使用的各种概念和语法是很重要的。让我们逐步分析这段代码:
-
导入 Scanner 类:
import java.util.Scanner;
这行代码导入了 Java 标准库中的
Scanner
类,它使我们能够从标准输入(通常是键盘)中读取用户的输入。 -
定义 LowerCase 类:
public class LowerCase {
这行代码定义了一个名为
LowerCase
的 Java 类,类名的首字母大写是因为在 Java 中类名通常以大写字母开头。 -
定义 main 方法:
public static void main(String[] args) {
这行代码定义了 Java 应用程序的入口点,即
main
方法。在 Java 程序中,main
方法是程序的起点,程序将从这里开始执行。 -
创建 Scanner 对象:
Scanner reader = new Scanner(System.in);
这行代码创建了一个名为
reader
的Scanner
对象,它将从标准输入流(System.in
)中读取用户的输入。 -
获取键盘输入的字符:
char uppercaseChar = reader.next().charAt(0);
这行代码通过
Scanner
对象reader
获取了用户输入的下一个字符串,并使用charAt(0)
方法获取该字符串的第一个字符,并将其存储在uppercaseChar
变量中。 -
转换大写字母为小写字母:
char lowercaseChar = Character.toLowerCase(uppercaseChar);
这行代码使用
Character.toLowerCase()
方法将大写字母转换为小写字母,并将结果存储在lowercaseChar
变量中。 -
输出转换后的小写字母:
System.out.print(lowercaseChar);
这行代码使用
System.out.print()
方法将转换后的小写字母输出到标准输出(通常是控制台)上。 -
关闭 Scanner 对象:
reader.close();
这行代码关闭了
Scanner
对象reader
,释放了与其关联的资源。
这段代码主要涉及了基本的输入输出、字符串操作、字符操作等基础知识。
拓展
类 与类方法
Character 类
当然,让我们来了解一下 Character
类的一些常用方法。Character
类提供了一系列静态方法来处理字符数据,这些方法允许你执行各种字符操作,比如大小写转换、字符类型判断等等。
下面是 Character
类的一些常用方法:
-
isLetter(char ch)
:判断指定字符是否为字母。char ch = 'A'; boolean isLetter = Character.isLetter(ch); // 返回 true
-
isDigit(char ch)
:判断指定字符是否为数字。char ch = '5'; boolean isDigit = Character.isDigit(ch); // 返回 true
-
isUpperCase(char ch)
:判断指定字符是否为大写字母。char ch = 'A'; boolean isUpperCase = Character.isUpperCase(ch); // 返回 true
-
isLowerCase(char ch)
:判断指定字符是否为小写字母。char ch = 'a'; boolean isLowerCase = Character.isLowerCase(ch); // 返回 true
-
toUpperCase(char ch)
:将指定字符转换为大写形式。char ch = 'a'; char upperCaseChar = Character.toUpperCase(ch); // 返回 'A'
-
toLowerCase(char ch)
:将指定字符转换为小写形式。char ch = 'A'; char lowerCaseChar = Character.toLowerCase(ch); // 返回 'a'
-
toString(char ch)
:将指定字符转换为字符串。char ch = 'A'; String str = Character.toString(ch); // 返回 "A"
-
getNumericValue(char ch)
:获取指定字符的数值。char ch = '5'; int numericValue = Character.getNumericValue(ch); // 返回 5
这些方法可以帮助你对字符进行各种常见操作,包括判断字符的属性、转换字符的大小写以及获取字符的数值等。通过熟练运用这些方法,你可以更方便地处理字符数据。
Scanner 类
Scanner
类提供了许多常用的方法,用于从输入流中读取数据。下面是一些 Scanner
类的常用方法:
-
next()
:读取输入的下一个单词(以空白字符分隔)并返回字符串。Scanner scanner = new Scanner(System.in); String word = scanner.next();
-
nextInt()
:读取输入的下一个整数,并返回int
类型的值。Scanner scanner = new Scanner(System.in); int number = scanner.nextInt();
-
nextDouble()
:读取输入的下一个双精度浮点数,并返回double
类型的值。Scanner scanner = new Scanner(System.in); double value = scanner.nextDouble();
-
nextLine()
:读取输入的下一行,并返回字符串,不包括换行符。Scanner scanner = new Scanner(System.in); String line = scanner.nextLine();
-
hasNext()
:检查输入中是否还有下一个单词(以空白字符分隔),返回true
或false
。Scanner scanner = new Scanner(System.in); boolean hasNext = scanner.hasNext();
-
hasNextInt()
:检查输入中是否还有下一个整数,返回true
或false
。Scanner scanner = new Scanner(System.in); boolean hasNextInt = scanner.hasNextInt();
-
hasNextLine()
:检查输入中是否还有下一行,返回true
或false
。Scanner scanner = new Scanner(System.in); boolean hasNextLine = scanner.hasNextLine();
-
useDelimiter(String pattern)
:设置用于分隔标记的模式字符串。默认情况下,空白字符用作分隔符。Scanner scanner = new Scanner(System.in); scanner.useDelimiter(",");
这些方法使得从不同类型的输入流(如标准输入、文件、字符串等)中读取数据变得非常方便。通过适当地使用这些方法,你可以轻松地处理各种输入数据。
Java基础
访问修饰符
Java 中有多种访问修饰符,它们决定了类、变量、方法或构造函数的访问权限。Java 中的访问修饰符包括以下几种:
-
public:表示对所有类都是可见的。即使是不同包中的类,只要是 public 访问修饰符修饰的成员,都可以被访问。
-
protected:表示对同一包中的类以及所有子类都是可见的。如果子类和父类不在同一个包中,那么子类只能访问 protected 修饰的成员,而不能访问父类中默认访问权限和 private 访问权限修饰的成员。
-
默认(package-private):如果没有使用任何访问修饰符(即不写任何修饰符),则表示该成员具有包级别的访问权限,即对同一包中的所有类是可见的。
-
private:表示只有在同一类中才可以访问。私有成员对类的使用者是隐藏的,类的使用者无法直接访问私有成员,而必须通过类提供的公共方法来访问私有成员。
这些访问修饰符允许你控制类的成员对其他类的可见性,从而实现封装、继承和多态等面向对象编程的特性。
定义方法的关键字
当定义一个方法时,你可以使用不同的关键字来指定方法的行为和特性。下面是每个关键字的详细解释:
-
public:
- 这个关键字表示方法对所有类都是可见的。
- 其他类可以访问公共方法,从而调用它们。
- 通常在需要提供给其他类使用的方法上使用
public
。
-
protected:
- 这个关键字表示方法对同一包中的类以及所有子类可见。
- 只有在同一包中或子类中才能访问受保护的方法。
- 通常在需要在继承层次结构中提供访问权限的方法上使用
protected
。
-
private:
- 这个关键字表示方法只对同一类中可见。
- 其他类无法直接访问私有方法。
- 通常在只在当前类内部使用的辅助方法上使用
private
。
-
abstract:
- 这个关键字表示方法没有实现,需要在子类中被覆盖。
- 抽象方法没有方法体,只有方法签名。
- 抽象方法必须在抽象类中声明,而抽象类本身可以有抽象方法和具体方法。
-
final:
- 这个关键字表示方法不能被子类重写。
- 如果一个类被声明为
final
,则其中的所有方法都自动成为final
方法,无法被子类修改。
-
static:
- 这个关键字表示方法属于类而不是实例,可以通过类名直接调用。
- 静态方法在内存中只有一份拷贝,被所有实例共享。
- 静态方法不能访问非静态的实例变量和实例方法,只能访问静态变量和静态方法。
-
synchronized:
- 这个关键字表示方法是同步的,只能被一个线程访问。
- 同步方法在多线程环境中用于防止竞态条件和数据不一致性。
- 只能在实例方法上使用
synchronized
,不能在静态方法上使用。
根据你的需求,选择适当的关键字来定义方法,以确保代码的可维护性、可读性和安全性。
实例和类
进一步解释一下实例(instance)和类(class)的概念。
在面向对象编程中,类是一种抽象的概念,它描述了具有相同属性和行为的一组对象的模板。类定义了对象的结构和行为,但它本身并不占用内存空间。
而实例则是类的具体化,是根据类创建的对象。当你创建一个类的实例时,会分配内存空间来存储该对象的属性和方法。每个实例都有自己的属性值,但它们共享相同的类结构和行为。
例如,假设我们有一个名为 Car
的类,它描述了汽车的属性和行为,比如颜色、型号、加速和刹车等。当我们创建一个具体的汽车对象时,比如一辆红色的奥迪车,这个对象就是 Car
类的一个实例。
现在来解释 static
关键字的含义:当我们将方法声明为 static
时,它意味着这个方法不再属于某个特定的实例,而是属于整个类。换句话说,无论有多少个类的实例被创建,static
方法在内存中只有一份拷贝,被所有实例共享。
因此,static
方法可以直接通过类名调用,而不需要通过实例。这使得静态方法在不需要实例的情况下也可以被调用,非常适合用于实现通用的工具方法或单例模式等场景。
静态方法与实例方法
实际上,Java 中并没有 "动态方法" 这个概念。你可能是混淆了 "静态方法" 和 "实例方法"。
-
静态方法:在内存中只有一份拷贝,被所有实例共享。无论创建了多少个类的实例,静态方法都只存在一份。静态方法可以直接通过类名调用,而不需要创建类的实例。
-
实例方法:每个类的实例都有自己的拷贝。当创建一个类的实例时,实例方法会被分配到该实例所占据的内存中。每个实例都有自己的方法副本,它们不会被其他实例共享。
因此,静态方法和实例方法的区别在于是否与类的实例相关。静态方法独立于类的任何实例,而实例方法与每个类的实例相关联。
静态方法不能访问非静态的实例变量和实例方法,只能访问静态变量和静态方法。为什么?
静态方法是类级别的方法,它们在内存中只有一份拷贝,被所有类的实例共享。由于静态方法不与任何特定的实例相关联,因此它们无法访问实例级别的变量和方法。
考虑到以下几点:
-
内存分配:静态方法在类加载时就已经被分配内存空间,而实例变量和实例方法是在创建对象实例时才会被分配内存空间。 因此,当静态方法被调用时,可能还没有任何实例被创建,因此无法访问实例级别的成员。
-
实例变量和实例方法需要特定的对象上下文:实例变量和实例方法是与特定的对象实例相关联的,它们需要通过对象引用来访问。 而静态方法没有这种对象上下文,无法直接访问实例级别的成员。
-
避免歧义:如果静态方法可以访问实例级别的成员,可能会导致歧义和混乱。因为静态方法属于类,而实例变量和实例方法属于对象实例,如果静态方法可以访问实例级别的成员,可能会使得代码逻辑不清晰,难以理解。
因此,为了保持代码的清晰性、简洁性和可维护性,Java 设计者规定静态方法只能访问静态变量和静态方法,而不能访问实例变量和实例方法。
类本身不占内存空间?为什么静态方法在类加载时就已经被分配内存空间,那不就是类占了内存空间
你提出了一个很好的问题,让我更清楚地解释一下:
在Java中,类本身确实是不会占用内存空间的。当JVM加载类时,会创建一个类的对象,称为类对象(Class Object),用于表示这个类的结构信息和方法代码。这个类对象包含了类的方法代码,包括静态方法。
静态方法被存储在类对象中,这意味着它们在内存中只有一份拷贝,与任何特定的实例无关。当类加载时,类对象被创建并放入方法区(Method Area)中,静态方法也随之加载到内存中,但它们不会随着每个实例的创建而复制。
类对象本身占用的内存空间相对较小,主要用于存储类的结构信息,例如类名、方法签名、常量池等。这个过程与实例创建时所需的内存空间是不同的。实例在堆内存中分配空间,它们包含了实例变量的值以及对方法的引用。
因此,虽然类对象会占用一些内存空间,但这个内存空间相对较小,并且是共享的,与实例的创建数量无关。而静态方法被存储在类对象中,因此在类加载时就已经被分配内存空间,但这并不意味着类的实例被创建了。
对象上下文
"对象上下文" 是指在面向对象编程中,方法执行时所处的特定对象的环境或状态。 换句话说,它是指方法被调用时所关联的对象及其属性和方法。
在 Java 中,每个对象都有自己的状态(即实例变量的值)和行为(即方法)。当调用对象的方法时,这个方法会在该对象的上下文中执行。在方法执行期间,方法可以访问并操作对象的属性,以及调用对象的其他方法。
例如,假设有一个 Car
类,它有一个 drive()
方法。当你调用 drive()
方法时,它会在特定的汽车对象上执行。在方法执行期间,drive()
方法可以访问该汽车对象的属性(如速度、方向等)并执行相应的操作。
因此,"对象上下文" 表示方法执行时所处的特定对象的环境,它确定了方法可以访问的属性和方法,以及方法的行为和结果。
返回类型
好的,让我详细介绍每种方法返回类型以及对应的关键字:
-
基本数据类型:
-
类型:基本数据类型是 Java 中最基本的数据类型,包括整型、浮点型、字符型和布尔型。
-
关键字:
int
:表示整型数据,范围为 -2147483648 到 2147483647。double
:表示双精度浮点型数据,范围为 -1.7976931348623157 x 10^308 到 1.7976931348623157 x 10^308。float
:表示单精度浮点型数据,范围为 -3.4028235 x 10^38 到 3.4028235 x 10^38。char
:表示字符数据,用单引号表示,如'A'
。boolean
:表示布尔型数据,只能是true
或false
。
-
-
引用数据类型:
-
类型:引用数据类型是指向对象的引用,包括类、接口和数组。
-
关键字:
- 类:自定义的类,使用类名表示。
- 接口:使用接口名表示。
- 数组:使用数据类型后加方括号
[]
表示,例如int[]
、String[]
。
-
-
特殊类型:
-
类型:特殊类型包括 void、泛型类型和枚举类型。
-
关键字:
void
:表示方法没有返回值。- 泛型类型:根据需要指定泛型的具体类型,例如
ArrayList<String>
、Map<Integer, String>
。 - 枚举类型:使用关键字
enum
来定义枚举类型,例如enum Day {MONDAY, TUESDAY, WEDNESDAY}
。
-
-
其他类型:
-
类型:其他类型包括 long、short、byte、String 和 Object 等。
-
关键字:
long
:表示长整型数据,范围为 -9223372036854775808 到 9223372036854775807。short
:表示短整型数据,范围为 -32768 到 32767。byte
:表示字节数据,范围为 -128 到 127。String
:表示字符串,用双引号表示,例如"Hello"
。Object
:表示任意对象类型,所有类都是 Object 类的子类。
-
这些就是 Java 中常见的方法返回类型及其关键字的详细介绍。在编写方法时,根据方法的功能和需要返回的数据类型,选择合适的返回类型和关键字。
返回引用数据类型解析
在 Java 中,方法确实可以返回引用数据类型,也就是返回对象的引用。这意味着方法返回的是指向对象的引用,而不是对象本身的拷贝。以下是一个示例:
public class Example {
public static void main(String[] args) {
// 调用方法,获取对象引用
MyClass obj = getObject();
// 使用返回的对象引用
obj.method();
}
// 返回引用数据类型的方法
public static MyClass getObject() {
// 创建对象
MyClass obj = new MyClass();
// 返回对象的引用
return obj;
}
}
// 定义一个简单的类
class MyClass {
public void method() {
System.out.println("This is a method of MyClass");
}
}
在上面的示例中,getObject()
方法返回了 MyClass
类的一个实例对象的引用。然后,该引用被赋值给 obj
变量,在 main()
方法中被使用来调用 method()
方法。
这种方式允许方法返回对象的引用,使得方法可以动态地创建和返回不同类型的对象,从而实现更灵活的编程。
main方法的参数
在 Java 中,main
方法的标准参数是 String[] args
,用于接收命令行参数。这里我会详细介绍几种不同的参数形式:
-
命令行参数:这是
main
方法的标准参数形式,用于从命令行接收参数。它是一个String
类型的数组,可以在程序运行时通过命令行传递参数给程序。public static void main(String[] args) { // your code }
-
不带参数的
main
方法:虽然不常见,但是在 Java 中你也可以定义一个不带参数的main
方法。这种情况下,程序不会接收命令行参数。public static void main() { // your code }
-
带可变参数的
main
方法:在 Java 5 中引入了可变参数,你也可以使用可变参数的形式定义main
方法。但这种形式不是标准的main
方法签名。public static void main(String... args) { // your code }
-
使用参数名称而不是
args
:虽然args
是约定俗成的参数名称,但实际上你可以使用任何合法的参数名称。只要遵循了方法的命名规范即可。public static void main(String[] arguments) { // your code }
总的来说,标准的 main
方法是第一种形式,即 public static void main(String[] args)
,但其他形式在特定情况下也是合法的。在实际开发中,通常还是遵循约定俗成的形式为佳,这样可以增强代码的可读性和可维护性。
main 方法里为什么要接受命令行参数?
main
方法接受命令行参数的主要原因是为了增加程序的灵活性和可配置性。通过命令行参数,用户可以在运行程序时动态地传递一些配置信息或参数给程序,从而影响程序的行为。
以下是一些常见的情况,可以使用命令行参数:
-
配置信息:用户可以通过命令行参数来配置程序的行为,例如指定输入文件的路径、输出文件的路径、日志级别等。
-
运行模式:用户可以通过命令行参数来指定程序的运行模式,例如调试模式、测试模式、生产模式等。
-
传递数据:用户可以通过命令行参数来传递数据给程序,例如需要处理的文件名、数据库连接信息等。
-
自定义功能:用户可以通过命令行参数来启用或禁用程序的某些功能,或者调整功能的参数。
通过接受命令行参数,程序可以更灵活地适应不同的使用场景和需求。这种灵活性使得程序更易于使用和维护,同时也提高了程序的可配置性和可扩展性。
main方法没有命令行参数就不能配置和运行吗?
不完全是这样。虽然命令行参数提供了一种灵活的方式来配置和运行程序,但并不是所有的程序都需要使用命令行参数。即使没有命令行参数,程序也可以进行配置和运行,只是配置的方式可能会有所不同。
以下是一些即使没有命令行参数也可以配置和运行的方式:
-
配置文件:程序可以读取配置文件来获取配置信息。配置文件通常是以文本形式存储的,可以包含各种配置项和参数。程序启动时可以读取配置文件,并根据配置文件中的内容进行配置和运行。
-
默认值:程序可以在代码中预先定义一些默认值作为配置的默认参数。如果没有提供命令行参数或配置文件,程序会使用这些默认值来进行配置和运行。
-
交互式输入:程序可以与用户进行交互,通过命令行或图形界面等方式来询问用户需要的配置信息。用户可以根据提示输入配置信息,然后程序根据输入的信息进行配置和运行。
虽然命令行参数是一种方便的配置方式,但并不是唯一的方式。根据程序的需求和设计,可以选择合适的配置方式来实现程序的配置和运行。
不同方式完成任务
当然,下面我会给你展示几种不同的方式来编写这段代码,虽然它们实现同样的功能,但采用了不同的方法和技巧:
方式一:使用字符数组
import java.util.Scanner;
public class LowerCase {
//Java程序的入口点,这个方法的签名在Java中是固定的
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
//
char[] input = reader.nextLine().toCharArray();
input[0] = Character.toLowerCase(input[0]);
System.out.print(input);
reader.close();
}
}
句句剖析
import java.util.Scanner;
import java.util.Scanner;
这行代码是 Java 中的导入语句。它的作用是告诉编译器在编译过程中需要使用 java.util.Scanner
这个类。
具体含义如下:
- import:关键字,用于告诉编译器要导入一个类或包。
- java.util.Scanner:是一个类的完整路径名。
java.util
是 Java 标准库中的一个包,而Scanner
是该包中的一个类,用于从输入流中获取用户的输入。
导入这个类后,你就可以在代码中使用 Scanner
类了,而不需要每次都写完整的路径,比如 java.util.Scanner scanner = new java.util.Scanner(System.in);
可以简化为 Scanner scanner = new Scanner(System.in);
。
public static void main(String[] args){}
public static void main(String[] args)
是 Java 程序的入口点。这个方法的签名在 Java 中是固定的,它的含义如下:
- public:表示该方法是公共的,可以被其他类访问。
- static:表示该方法是静态的,可以直接通过类名调用,而不需要实例化对象。
- void:表示该方法没有返回值。
- main:是方法的名称,固定为 "main"。
- String[] args:是方法的参数,是一个字符串数组,用于传递命令行参数。
在一个 Java 程序中,当程序运行时,Java 虚拟机(JVM)会首先调用 main
方法作为程序的入口点,开始执行程序的代码逻辑。因此,main
方法是 Java 程序的起点。
深度解析:
当我们编写一个 Java 程序时,我们需要指定程序的入口点,即程序从哪里开始执行。在 Java 中,程序的入口点就是一个名为 main
的方法。
让我们来详细解释一下 public static void main(String[] args)
这句话的含义:
- public:这是一个访问修饰符,表示该方法是公共的,可以被其他类访问。
- static:这是一个关键字,表示该方法是静态的,即它属于类而不是属于对象。因此,可以通过类名直接调用这个方法,而不需要创建类的实例。
- void:这是方法的返回类型。
void
表示这个方法没有返回值,即它不会返回任何数据。 - main:这是方法的名称,即
main
方法。 - (String[] args):这是方法的参数列表。在这里,
(String[] args)
表示这个方法接受一个字符串数组作为参数,这个参数通常用于传递命令行参数。
因此,整个语句 public static void main(String[] args)
表示定义了一个名为 main
的方法,它是程序的入口点,并且接受一个字符串数组作为参数。当你运行一个 Java 程序时,JVM 会首先寻找并执行 main
方法中的代码,从而启动程序的执行。
Scanner reader = new Scanner(System.in);
这句话创建了一个名为 reader
的 Scanner
对象,用于从标准输入(通常是键盘)读取用户输入的数据。
让我逐步解释:
-
Scanner
:这是 Java 中的一个类,位于java.util
包中。Scanner
类用于从各种输入源(如文件、字符串、输入流等)读取数据,并将其解析为各种数据类型。 -
reader
:这是创建的Scanner
对象的名称。在这个例子中,我们给这个对象取名为reader
,但你也可以使用其他合适的名称。 -
= new Scanner(System.in)
:这部分代码创建了一个新的Scanner
对象,并将其分配给reader
变量。System.in
表示标准输入流,通常是用户在控制台(命令行界面)中输入的内容。因此,new Scanner(System.in)
表示创建一个Scanner
对象,它会读取用户在控制台中输入的数据。
综合起来,Scanner reader = new Scanner(System.in);
这句话的意思是创建了一个名为 reader
的 Scanner
对象,该对象用于从控制台读取用户输入的数据。
char[] input = reader.nextLine().toCharArray();
让我一步步解释这行代码:
-
reader.nextLine()
:调用Scanner
对象reader
的nextLine()
方法。这个方法用于读取控制台输入的一行文本,并将其作为一个字符串返回。 -
.toCharArray()
:String
类的一个方法,用于将字符串转换为字符数组。在这里,我们使用它将nextLine()
方法返回的字符串转换为字符数组。 -
char[] input = ...;
:这一部分是声明和初始化一个名为input
的字符数组。字符数组是一种用于存储字符序列的数据结构。
综合起来,char[] input = reader.nextLine().toCharArray();
这行代码的意思是从控制台读取用户输入的一行文本,并将其转换为字符数组存储在名为 input
的变量中。
小知识
nextLine() 是什么
nextLine()
是 Java 中 Scanner
类的一个方法,用于从标准输入(通常是键盘)读取一行输入并将其作为字符串返回。
具体来说,nextLine()
方法会扫描输入中的下一行,直到遇到换行符为止(包括换行符),然后返回该行的内容,但不包括换行符本身。
在我们的代码中,scanner.nextLine()
用于获取用户输入的整行字符,然后我们通过其他方法来处理这个输入。
nextLine().toCharArray()是什么?
nextLine()
是 Scanner
类中的方法,用于从标准输入读取一行文本,并返回一个字符串表示该行文本。
toCharArray()
是字符串类 String
的方法,用于将字符串转换为字符数组。
因此,nextLine().toCharArray()
这个表达式的意思是:
- 首先,从标准输入读取一行文本。
- 然后,将这行文本转换为字符数组。
换句话说,nextLine().toCharArray()
返回的是表示用户输入的整行文本的字符数组。
字符串转化为字符数组,存储方式是变了吗?原来输入的字符串的存储空间是释放了吗?
当你调用 toCharArray()
方法将字符串转换为字符数组时,实际上创建了一个新的字符数组,其中包含了字符串中的所有字符。这个字符数组是新分配的内存空间,与原始的字符串对象不同。
原始输入的字符串对象并没有被释放,它仍然存在于内存中。只是通过 toCharArray()
方法得到了一个新的字符数组对象,它们之间没有直接的关系。
换句话说,原始字符串对象和新创建的字符数组对象是两个不同的对象,它们分别占用不同的内存空间。原始字符串对象在内存中仍然存在,只是不再被新的引用指向。因此,如果没有其他引用指向原始字符串对象,它将被 Java 的垃圾回收机制释放掉,释放其占用的内存空间。
更深入的解释
理解 Java 中的内存分配涉及到堆内存和栈内存的概念。让我详细解释一下:
-
栈内存(Stack Memory):
- 栈内存用于存储方法调用的局部变量、方法参数和方法调用的执行上下文。
- 当一个方法被调用时,会在栈内存中分配一块内存空间,称为栈帧(Stack Frame),用于存储方法的局部变量和方法参数。
- 当方法执行完成时,栈帧被弹出,释放其占用的栈内存空间。
-
堆内存(Heap Memory):
- 堆内存用于存储对象实例和数组等动态分配的数据。
- 当使用
new
关键字创建对象时,对象被分配在堆内存中。 - 对象在堆内存中存储的是其成员变量的值,以及指向方法区中方法的引用等信息。
下面是创建字符串对象并转换为字符数组时的内存分配步骤:
-
创建字符串对象:
- 当你输入字符串时,在堆内存中会创建一个字符串对象,用于存储这个字符串的值。
- 字符串对象包含了字符序列的内容以及一些其他的信息,例如字符串的长度等。
-
调用
toCharArray()
方法:- 当调用
toCharArray()
方法时,会创建一个新的字符数组对象。 - 这个字符数组对象在堆内存中分配了一块新的内存空间,用于存储字符串中的字符。
- 当调用
-
复制字符数据:
toCharArray()
方法会将字符串中的字符复制到新创建的字符数组中。- 这个过程涉及到将字符串的字符逐个复制到字符数组中的不同位置。
-
原始字符串对象的释放(如果适用):
- 如果没有其他引用指向原始的字符串对象,它将被 Java 的垃圾回收机制自动释放掉。
- 原始的字符串对象所占用的堆内存空间将被释放,以便其他对象可以使用。
综上所述,创建字符串对象并转换为字符数组时,会涉及到堆内存中的对象创建、复制数据以及释放空间等步骤。
方式二:使用字符串操作
import java.util.Scanner;
public class LowerCase {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
String input = reader.nextLine();
String output = Character.toLowerCase(input.charAt(0)) + input.substring(1);
System.out.print(output);
reader.close();
}
}
方式三:使用 StringBuilder
import java.util.Scanner;
public class LowerCase {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
StringBuilder input = new StringBuilder(reader.nextLine());
input.setCharAt(0, Character.toLowerCase(input.charAt(0)));
System.out.print(input);
reader.close();
}
}
方式四:使用字符流
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class LowerCase {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
char firstChar = (char) reader.read();
firstChar = Character.toLowerCase(firstChar);
System.out.print(firstChar);
reader.close();
}
}
方式五:使用字符包装类
import java.util.Scanner;
public class LowerCase {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
Character firstChar = reader.nextLine().charAt(0);
firstChar = Character.toLowerCase(firstChar);
System.out.print(firstChar);
reader.close();
}
}
这些方式都能达到同样的目的,但采用了不同的技术和方法。选择合适的方式取决于你对不同功能的熟悉程度以及个人喜好。
不同方式的全注释文档
当然,让我们逐个解释每种方式,并添加详细的注释:
方式一:使用字符数组
import java.util.Scanner;
public class LowerCase {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
// 从键盘读取输入,并转换为字符数组
char[] input = reader.nextLine().toCharArray();
// 将数组中第一个字符转换为小写
input[0] = Character.toLowerCase(input[0]);
// 输出字符数组
System.out.print(input);
reader.close();
}
}
方式二:使用字符串操作
import java.util.Scanner;
public class LowerCase {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
// 从键盘读取输入
String input = reader.nextLine();
// 将字符串第一个字符转换为小写,然后拼接原字符串后面的部分
String output = Character.toLowerCase(input.charAt(0)) + input.substring(1);
// 输出结果
System.out.print(output);
reader.close();
}
}
方式三:使用 StringBuilder
import java.util.Scanner;
public class LowerCase {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
// 从键盘读取输入,并将其转换为 StringBuilder 对象
StringBuilder input = new StringBuilder(reader.nextLine());
// 将 StringBuilder 对象的第一个字符转换为小写
input.setCharAt(0, Character.toLowerCase(input.charAt(0)));
// 输出结果
System.out.print(input);
reader.close();
}
}
方式四:使用字符流
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class LowerCase {
public static void main(String[] args) throws IOException {
// 创建 BufferedReader 对象用于从标准输入读取字符
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
// 读取输入的第一个字符
char firstChar = (char) reader.read();
// 将第一个字符转换为小写
firstChar = Character.toLowerCase(firstChar);
// 输出结果
System.out.print(firstChar);
// 关闭 BufferedReader 对象
reader.close();
}
}
方式五:使用字符包装类
import java.util.Scanner;
public class LowerCase {
public static void main(String[] args) {
Scanner reader = new Scanner(System.in);
// 从键盘读取输入,并取第一个字符
Character firstChar = reader.nextLine().charAt(0);
// 将第一个字符转换为小写
firstChar = Character.toLowerCase(firstChar);
// 输出结果
System.out.print(firstChar);
reader.close();
}
}
以上是对每种方式的解释以及带有详细注释的代码。每种方式都使用了不同的技术和方法,你可以根据自己的喜好和项目的需求来选择最适合的方式。
不同方式所运用到的技术
让我更详细地解释每种方式所涉及的知识:
方式一:使用字符数组
- Scanner 类:用于从标准输入读取用户的输入。
- toCharArray() 方法:将字符串转换为字符数组,方便对单个字符进行操作。
- Character 类:提供了许多用于字符操作的静态方法,如
toLowerCase()
方法用于将字符转换为小写形式。
方式二:使用字符串操作
- Scanner 类:同样用于从标准输入读取用户的输入。
- charAt() 方法:用于获取字符串中指定位置的字符。
- Character 类:同样用于将字符转换为小写形式。
方式三:使用 StringBuilder
- Scanner 类:用于从标准输入读取用户的输入。
- StringBuilder 类:用于处理可变的字符串序列,提供了
setCharAt()
方法来修改指定位置的字符。
方式四:使用字符流
- BufferedReader 类:用于从输入流中读取文本数据,比起 Scanner 类更高效。
- InputStreamReader 类:用于将字节流转换为字符流。
- IOException 异常:因为读取输入流可能会出现 I/O 错误,因此需要处理可能抛出的 IOException 异常。
- 字符类型转换:通过强制类型转换将 int 类型的字符转换为 char 类型。
方式五:使用字符包装类
- Scanner 类:同样用于从标准输入读取用户的输入。
- Character 类:用于将字符转换为小写形式。
以上方式涉及到了输入输出、字符操作、异常处理等多种知识点。选择合适的方式取决于你对这些知识点的熟悉程度以及项目的需求。
标签:字符,Java,Scanner,小写字母,System,char,reader,方法 From: https://www.cnblogs.com/oakes/p/18109445