编程是指使用计算机语言编写代码,以实现计算机程序的过程。编程是一种创造性的活动,它要求程序员具备逻辑思维、分析问题和解决问题的能力。编程可以用于开发各种类型的软件,例如桌面应用程序、移动应用程序、网站、游戏等。
看Java官方的图片,Jdk中包括了Jre,Jre中包括了JVM
JDK :JDK 是 Java 开发工具包,包括了 Java 编译器、Java 虚拟机、开发工具和类库等组件,是 Java 程序员进行开发、编译和调试 Java 程序必须的工具。Jdk还包括了一些Jre之外的东西 ,就是这些东西帮我们编译Java代码的, 还有就是监控Jvm的一些工具 Java Development Kit是提供给Java开发人员使用的,其中包含了Java的开发工具,也包括了JRE。所以安装了JDK,就无需再单独安装JRE了。其中的开发工具:编译工具(javac.exe),打包工具(jar.exe)等
JRE :JRE 是 Java 运行时环境,包括了 Java 虚拟机和标准类库等组件,用于运行已经编译好的 Java 程序。Jre大部分都是 C 和 C++ 语言编写的,他是我们在编译java时所需要的基础的类库 Java Runtime Environment包括Java虚拟机和Java程序所需的核心类库等。
如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。
JVM:JVM 是 Java 虚拟机,是 Java 程序运行的环境,它可以将字节码文件解释成机器码并执行。JVM 是 Java 语言实现跨平台的关键,因为它可以在不同的操作系统上运行相同的字节码文件。
跨平台性是指软件可以在多个不同的操作系统和硬件平台上运行,而不需要进行修改或编译。
Java 语言具有很好的跨平台性,这是因为 Java 代码是通过编译器编译成字节码,字节码可以在任何支持 Java 虚拟机(JVM)的平台上运行。
Java 编译器将源代码编译成字节码文件,字节码文件包含了 Java 虚拟机可以理解的指令集。
Java 虚拟机是一个软件,它可以在不同的操作系统和硬件平台上运行,它负责将字节码文件解释成机器码并执行。
Java 虚拟机提供了标准的类库,程序员可以使用这些类库进行开发,这些类库在不同的平台上都有相同的实现。
因为 Java 虚拟机和标准类库都是跨平台的,所以 Java 程序可以在任何支持 Java 虚拟机的平台上运行,而不需要进行修改或编译。
简单易学:Java 语言的语法简单明了,易于学习和理解。
面向对象:Java 语言是一种纯粹的面向对象编程语言,支持封装、继承和多态等特性。
平台无关性:Java 语言的编译器将源代码编译成字节码,字节码可以在任何支持 Java 虚拟机(JVM)的平台上运行,因此具有很好的移植性。
安全性高:Java 语言提供了严格的安全机制,可以防止程序在运行时对系统造成损害。
高性能:Java 语言的虚拟机具有优秀的垃圾回收机制和即时编译技术,可以提高程序的执行效率。
多线程支持:Java 语言提供了多线程编程的支持,可以方便地实现多线程程序。
开源免费:Java 语言是一种开源免费的编程语言,可以免费使用、修改和分发。
丰富的类库:Java 语言提供了丰富的类库,包括了各种常用的数据结构、算法、网络、图形界面、数据库等组件,可以方便地进行程序开发。
字节码:字节码是一种中间代码,是 Java 语言编译后的代码,它不是针对特定的硬件和操作系统,而是针对 Java 虚拟机(JVM)的。Java源代码经过虚拟机编译器编译后产生的文件(即扩展为.class的文件),字节码可以在任何支持 Java 虚拟机的平台上运行。
采用字节码的好处:
采用字节码的最大好处是可以实现平台无关性。因为 Java 代码是先编译成字节码,然后由 JVM 运行,所以 Java 代码可以在任何支持 JVM 的平台上运行。这种机制使得 Java 语言具有很好的移植性,可以方便地将 Java 程序部署到不同的操作系统和硬件平台上。
采用字节码还可以提高程序的安全性。因为字节码是一种中间代码,不是直接可执行的机器 码,所以可以防止程序被恶意修改或破解。同时,Java 虚拟机还提供了严格的安全机制,可 以防止程序在运行时对系统造成损害。
先看下java中的编译器和解释器:
Java中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟机器。这台虚拟的机器在任何平台上都提供给编译程序一个的共同的接口。编译程序只需要面向虚拟机,生成虚拟机能够理解的代码,然后由解释器来将虚拟机代码转换为特定系统的机器码执行。在Java中,这种供虚拟机理解的代码叫做字节码(即扩展为.class的文件),它不面向任何特定的处理器,只面向虚拟机。每一种平台的解释器是不同的,但是实现的虚拟机是相同的。Java源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行,这就是上面提到的Java的特点的编译与解释并存的解释。
Java源代码---->编译器---->jvm可执行的Java字节码(即虚拟指令)---->jvm---->jvm中解释器----->机器可执行的二进制机器码---->程序运行。
public static void main(String[] args)
其中,public 表示该方法是公共方法,可以从其他类中访问;static 表示该方法是静态方法,可以直接通过类名调用;void 表示该方法不返回任何值;main 是方法的名称;String[] args 表示传递给 main() 方法的参数,它是一个字符串数组。
主类是 Java 程序的重要组成部分,也是程序入口点的定义所在。
运行环境不同:Java 应用程序是在 Java 虚拟机(JVM)上运行的,而小程序是在微信客户端上运行的。
开发语言不同:Java 应用程序使用 Java 语言开发,而小程序使用 WXML、WXSS 和 JavaScript 等语言开发。
应用场景不同:Java 应用程序通常用于开发大型应用程序,如企业级应用、Web 应用、移动应用等,而小程序通常用于开发轻量级应用,如小游戏、小工具等。
功能限制不同:小程序的功能相对较为简单,只能访问微信提供的 API 接口,而 Java 应用程序的功能相对较为丰富,可以访问各种系统资源和第三方库。
开发工具不同:Java 应用程序的开发工具通常是 Eclipse、IntelliJ IDEA 等集成开发环境(IDE),而小程序的开发工具是微信开发者工具。
运行环境不同:Java 程序是在 Java 虚拟机(JVM)上运行的,而 C++ 程序是编译成本地机器码后直接运行的。
内存管理方式不同:Java 采用自动垃圾回收机制,程序员不需要手动管理内存,而 C++ 需要手动分配和释放内存。
语言特性不同:Java 是一种面向对象的编程语言,支持类、接口、继承、多态等特性,而 C++ 同样支持面向对象编程,但也支持过程式编程。
平台兼容性不同:Java 程序具有很好的平台无关性,可以在任何支持 Java 虚拟机的平台上运行,而 C++ 程序需要在不同平台上重新编译才能运行。
异常处理方式不同:Java 提供了强大的异常处理机制,可以方便地捕获和处理异常,而 C++ 的异常处理相对较为简单。
许可证:Oracle JDK 使用的是 Oracle 公司的专有许可证,而 OpenJDK 使用的是 GPL 许可证。
支持和更新:Oracle JDK 提供商 Oracle 公司提供商业支持和更新,而 OpenJDK 由社区维护,更新较为频繁。
功能差异:Oracle JDK 包含一些商业特性,如 Java Flight Recorder、Java Mission Control 等,而 OpenJDK 不包含这些特性。
安全性:Oracle JDK 提供商 Oracle 公司对 JDK 进行安全性测试和修复,而 OpenJDK 的安全性由社区维护。
兼容性:Oracle JDK 与 Java SE 规范的兼容性更好,而 OpenJDK 的兼容性可能会受到社区开发人员的影响。
Java有哪些数据类型
定义:Java语言是强类型语言,对于每一种数据都定义了明确的具体的数据类型,在内存中分配了不同大小的内存空间。
分类
Java基本数据类型图
java 中的 Math.round(-1.5) 等于多少?
Math提供了三个与取整有关的方法:ceil、floor、round
1、ceil:向上取整;
Math.ceil(11.3) = 12;
Math.ceil(-11.3) = 11;
2、floor:向下取整;
Math.floor(11.3) = 11;
Math.floor(-11.3) = -12;
3、round:四舍五入;
加0.5然后向下取整。
Math.round(11.3) = 11;
Math.round(11.8) = 12;
Math.round(-11.3) = -11;
Math.round(-11.8) = -12;
float f=3.4;是否正确?
short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?
对于 short s1 = 1; s1 = s1 + 1;由于 1 是 int 类型,因此 s1+1 运算结果也是 int型,需要强制转换类型才能赋值给 short 型。
而 short s1 = 1; s1 += 1;可以正确编译,因为 s1+= 1;相当于 s1 = (short(s1 + 1);其中有隐含的强制类型转换。
Java语言采用何种编码方案?有何特点?
什么是Java注释
定义:Java 注释是在编写 Java 代码时添加的文本,用于解释代码的作用、功能、实现方式等信息。
分类
// 这是一个单行注释
int a = 10; // 这是一行代码和一个单行注释
/*
这是一个多行注释,
可以跨越多行。
*/
int a = 10;
/**
* 这是一个文档注释,用于生成 API 文档。
* @param a 参数 a 的作用是什么
* @return 返回值的作用是什么
*/
public int method(int a) {
return a + 1;
}
注意事项:多行和文档注释都不能嵌套使用。
Java 运算符是用于执行算术、逻辑、位、关系等运算的符号。
int a = 10;
int b = 3;
int c = a + b; // c 的值为 13
int d = a - b; // d 的值为 7
int e = a * b; // e 的值为 30
int f = a / b; // f 的值为 3
int g = a % b; // g 的值为 1
int a = 10;
int b = 3;
boolean c = a > b; // c 的值为 true
boolean d = a >= b; // d 的值为 true
boolean e = a < b; // e 的值为 false
boolean f = a <= b; // f 的值为 false
boolean g = a == b; // g 的值为 false
boolean h = a != b; // h 的值为 true
boolean a = true;
boolean b = false;
boolean c = a && b; // c 的值为 false
boolean d = a || b; // d 的值为 true
boolean e = !a; // e 的值为 false
int a = 10; // 二进制表示为 1010
int b = 3; // 二进制表示为 0011
int c = a & b; // c 的值为 2,二进制表示为 0010
int d = a | b; // d 的值为 11,二进制表示为 1011
int e = a ^ b; // e 的值为 9,二进制表示为 1001
int f = ~a; // f 的值为 -11,二进制表示为 11111111111111111111111111110101
int g = a << 2; // g 的值为 40,二进制表示为 101000
int h = a >> 2; // h 的值为 2,二进制表示为 10
int i = a >>> 2; // i 的值为 2,二进制表示为 10
int a = 10;
a += 3; // 等价于 a = a + 3;
a -= 3; // 等价于 a = a - 3;
a *= 3; // 等价于 a = a * 3;
a /= 3; // 等价于 a = a / 3;
a %= 3; // 等价于 a = a % 3;
Java 关键字是指在 Java 语言中具有特殊含义和用途的保留字,不能用作标识符或变量名。
访问修饰符关键字:用于控制类、方法、变量等元素的访问权限,包括 public、private、protected 和 default。
类、方法、变量声明关键字:用于声明类、方法、变量等元素,包括 class、interface、enum、extends、implements、throws、void、return、new、this 等。
流程控制关键字:用于控制程序的流程和执行顺序,包括 if、else、switch、case、default、for、while、do、break、continue、return 等。
异常处理关键字:用于处理程序运行时出现的异常情况,包括 try、catch、finally、throw 和 throws。
其他关键字:包括 static、final、abstract、synchronized、volatile 等,用于修饰类、变量、方法等元素的特性和行为。
final 的作用?
final 是一个关键字,用于修饰变量、方法和类。
final finally finalize区别?
final:final 是一个关键字,用于修饰变量、方法和类。final 修饰的变量表示常量,其值不能被修改,一旦被赋值就不能改变。final 修饰的方法表示该方法不能被子类重写。final 修饰的类表示该类不能被继承。
finally:finally 是一个关键字,用于定义在 try-catch 语句块中的一个代码块,无论是否捕获异常,finally 中的代码都会被执行。finally 常用于资源释放、清理等操作。
finalize:finalize 是 Object 类中的一个方法,用于在对象被垃圾回收之前执行一些清理操作。finalize 方法是由垃圾回收器在回收对象时自动调用的,程序员不能直接调用。
this关键字的用法
this 是一个关键字,用于表示当前对象的引用。可以理解为:指向对象本身的一个指针。
this的用法在java中大体可以分为3种:
1.用于区分局部变量和成员变量:当成员变量和局部变量同名时,使用 this 关键字可以区分它们。
public class Person {
private String name; // 成员变量 name
public void setName(String name) { // 设置成员变量 name 的值
this.name = name; // 使用 this 关键字区分成员变量和局部变量
}
}
2.用于在构造方法中调用其他构造方法:当一个类有多个构造方法时,可以使用 this 关键字在一个构造方法中调用另一个构造方法。
public class Person {
private String name;
private int age;
public Person() { // 无参构造方法
this("Unknown", 0); // 调用另一个构造方法
}
public Person(String name, int age) { // 有参构造方法
this.name = name;
this.age = age;
}
}
3.用于返回当前对象的引用:在某些情况下,需要返回当前对象的引用,可以使用 this 关键字。
public class Person {
private String name;
private int age;
public Person setName(String name) {
this.name = name;
return this; // 返回当前对象的引用
}
public Person setAge(int age) {
this.age = age;
return this; // 返回当前对象的引用
}
}
super关键字的用法
super 是一个关键字,用于表示父类的引用。
super也有三种用法:
1.调用父类的构造方法:在子类的构造方法中,可以使用 super 关键字调用父类的构造方法。
public class Student extends Person {
private String school;
public Student(String name, int age, String school) {
super(name, age); // 调用父类的构造方法
this.school = school;
}
}
2.调用父类的成员变量和成员方法:在子类中,可以使用 super 关键字调用父类的成员变量和成员方法。
public class Student extends Person {
private String school;
public Student(String name, int age, String school) {
super(name, age);
this.school = school;
}
public void printInfo() {
System.out.println(super.getName()); // 调用父类的 getName 方法
System.out.println(super.age); // 访问父类的 age 成员变量
}
}
3.在子类中访问父类的构造方法、成员变量和成员方法:在子类中,可以使用 super 关键字访问父类的构造方法、成员变量和成员方法。
public class Student extends Person {
private String school;
public Student(String name, int age, String school) {
super(name, age);
this.school = school;
}
public void printInfo() {
System.out.println(super.getName()); // 调用父类的 getName 方法
System.out.println(super.age); // 访问父类的 age 成员变量
super.printInfo(); // 调用父类的 printInfo 方法
}
}
this与super的区别
super 关键字用于表示父类的引用,可以用于以下几个方面:
this 和 super 的区别在于它们所表示的对象不同。this 表示当前对象,super 表示父类的对象。
此外,this 和 super 还有一些使用上的限制。例如,this 和 super 不能同时出现在同一个构造方法的第一行,因为它们都表示对构造方法的调用。另外,在静态方法中不能使用 this 和 super 关键字,因为静态方法属于类,而不属于对象。
this 关键字用于表示当前对象的引用,可以用于以下几个方面:
static存在的主要意义
共享内存空间:static 可以用来修饰成员变量和成员方法,被 static 修饰的成员变量和成员方法属于类,而不属于对象。这意味着,不同的对象共享同一个 static 成员变量和 static 成员方法,它们都存储在类的内存空间中。这样可以节省内存空间,提高程序的执行效率。
方便访问:被 static 修饰的成员变量和成员方法可以直接通过类名访问,而不需要创建对象。这样可以方便地访问类的静态成员,而不需要创建对象。
类型限定:被 static 修饰的成员变量和成员方法属于类,而不属于对象。这意味着,它们可以被类的所有对象共享。同时,static 成员变量和 static 成员方法也可以通过类名直接访问,这样可以对类进行类型限定,避免了对象的创建和类型转换的开销。
静态块初始化:static 还可以用来定义静态块,在类加载时执行。静态块可以用来初始化静态成员变量和执行一些静态操作。
static应用场景
共享数据:被 static 修饰的成员变量可以被所有对象共享,这样可以避免创建多个对象造成内存浪费。例如,在一个多线程的程序中,可以使用 static 成员变量来实现多个线程共享数据。
工具类:被 static 修饰的成员方法可以直接通过类名调用,而不需要创建对象。这样可以方便地定义一些工具类,例如 Math 类中的 abs() 方法就是一个静态方法。
常量定义:被 static 和 final 修饰的成员变量可以定义为常量,常量不会改变,可以提高程序的可读性和可维护性。例如,Java 中的 Math.PI 就是一个静态常量。
静态块初始化:静态块可以用来初始化静态成员变量和执行一些静态操作。例如,在一个多线程的程序中,可以使用静态块来初始化一些静态变量。
static注意事项
if (condition) {
// 如果条件为真,执行这里的代码
} else {
// 如果条件为假,执行这里的代码
}
switch (variable) {
case value1:
// 如果 variable 等于 value1,执行这里的代码
break;
case value2:
// 如果 variable 等于 value2,执行这里的代码
break;
default:
// 如果 variable 不等于任何一个值,执行这里的代码
break;
}
for (initialization; condition; update) {
// 循环体
}
while (condition) {
// 循环体
}
do {
// 循环体
} while (condition);
break ,continue ,return 的区别及作用
break 跳出总上一层循环,不再执行循环(结束当前的循环体)
continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件)
return 程序返回,不再执行下面的代码(结束当前的方法 直接返回)
在 Java 中,如何跳出当前的多重嵌套循环
在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break 语句,即可跳出外层循环。例如:
outer:
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
if (i * j > 50) {
break outer; // 跳出外层循环
}
}
}
面向对象和面向过程的区别
面向过程:
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展
面向对象:
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
缺点:性能比面向过程低
面向过程是具体化的,流程化的,解决一个问题,你需要一步一步的分析,一步一步的实现。
面向对象是模型化的,你只需抽象出一个类,这是一个封闭的盒子,在这里你拥有数据也拥有解决问题的方法。需要什么功能直接使用就可以了,不必去一步一步的实现,至于这个功能是如何实现的,管我们什么事?我们会用就可以了。
面向对象的底层其实还是面向过程,把面向过程抽象成类,然后封装,方便我们使用的就是面向对象了。
面向对象的特征主要有以下几个方面:
封装(Encapsulation):将数据和行为封装在对象内部,通过接口来实现对外的访问。封装可以保护对象的内部状态,防止外部直接访问和修改,提高了程序的安全性和可靠性。
继承(Inheritance):通过继承来复用已有的代码,并在此基础上进行扩展。继承可以提高代码的复用性和可维护性,避免了重复编写相似的代码。
多态(Polymorphism):同一个方法可以根据不同的对象表现出不同的行为。多态可以提高程序的灵活性和可扩展性,使得程序可以适应不同的场景和需求。
这三大特性是面向对象编程的核心,也是面向对象编程与面向过程编程的重要区别。在实际开发中,应该充分利用这三大特性,以提高程序的可读性、可维护性和可扩展性。
什么是多态机制?
多态是面向对象编程中的一个重要特性,它可以让同一个方法在不同的对象上表现出不同的行为,从而提高程序的灵活性和可扩展性。
Java多态的实现
重载(Overloading):同一个类中可以定义多个同名的方法,但这些方法的参数类型、个数或顺序必须不同。编译器会根据调用时的参数类型、个数或顺序来选择相应的方法。这种多态称为编译时多态或静态多态。
重写(Overriding):子类可以重写父类中的方法,以实现自己的行为。在调用这个方法时,会根据对象的实际类型来选择相应的方法。这种多态称为运行时多态或动态多态。
通过多态机制,可以实现代码的复用和扩展,使得程序更加灵活和可维护。需要注意的是,要想使用多态机制,必须满足一些前提条件,比如必须有继承关系、方法名和参数列表必须相同等等。同时,在使用多态时也要注意避免一些常见的问题,比如类型转换异常、方法不存在等等。
abstract class Animal {
public abstract void speak();
}
class Cat extends Animal {
@Override
public void speak() {
System.out.println("Cat meows!");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Cat();
animal.speak(); // 输出 Cat meows!
}
}
面向对象五大基本原则是什么?
单一职责原则(Single Responsibility Principle,SRP):一个类应该只有一个职责,即一个类只负责一项功能。
开闭原则(Open-Closed Principle,OCP):一个类应该对扩展开放,对修改关闭。即当需要改变一个类的功能时,应该通过添加新的代码来扩展其功能,而不是修改原有的代码。
里氏替换原则(Liskov Substitution Principle,LSP):子类对象可以替换父类对象出现在程序中的任何地方,并且保证程序的逻辑不变。即子类应该可以完全替代父类并且不影响程序的正确性。
接口隔离原则(Interface Segregation Principle,ISP):客户端不应该依赖于它不需要的接口。即一个类不应该强制实现它不需要的接口,而应该将接口拆分成更小的和更具体的接口。
依赖倒置原则(Dependency Inversion Principle,DIP):高层模块不应该依赖于低层模块,它们都应该依赖于抽象。即抽象不应该依赖于具体实现,具体实现应该依赖于抽象。
抽象类和接口的对比
抽象类是用来捕捉子类的通用特性的。接口是抽象方法的集合。
从设计层面来说,抽象类是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。
相同点
不同点
参数 | 抽象类 | 接口 |
---|---|---|
声明 | 抽象类使用abstract关键字声明 | 接口使用interface关键字声明 |
实现 | 子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现 | 子类使用implements关键字来实现接口。它需要提供接口中所有声明的方法的实现 |
构造器 | 抽象类可以有构造器 | 接口不能有构造器 |
访问修饰符 | 抽象类中的方法可以是任意访问修饰符 | 接口方法默认修饰符是public。并且不允许定义为 private 或者 protected |
多继承 | 一个类最多只能继承一个抽象类 | 一个类可以实现多个接口 |
字段声明 | 抽象类的字段声明可以是任意的 | 接口的字段默认都是 static 和 final 的 |
备注:Java8中接口中引入默认方法和静态方法,以此来减少抽象类和接口之间的差异。
现在,我们可以为接口提供默认实现的方法了,并且不用强制子类来实现它。
普通类和抽象类有哪些区别?
抽象类能使用 final 修饰吗?
创建一个对象用什么关键字?对象实例与对象引用有何不同?
成员变量与局部变量的区别有哪些
变量:在程序执行的过程中,在某个范围内其值可以发生改变的量。从本质上讲,变量其实是内存中的一小块区域
成员变量:方法外部,类内部定义的变量
局部变量:类的方法中的变量。
成员变量和局部变量的区别
作用域
存储位置
生命周期
初始值
在调用子类构造方法之前会先调用父类没有参数的构造方法,其目的是?
一个类的构造方法的作用是什么?若一个类没有声明构造方法,改程序能正确执行吗?为什么?
构造方法有哪些特性?
名字与类名相同;
没有返回值,但不能用void声明构造函数;
生成类的对象时自动执行,无需调用。
静态变量和实例变量区别
静态变量: 静态变量由于不属于任何实例对象,属于类的,所以在内存中只会有一份,在类的加载过程中,JVM只为静态变量分配一次内存空间。
实例变量: 每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就有几份成员变量。
静态变量与普通变量区别
static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
还有一点就是static成员变量的初始化顺序按照定义的顺序进行初始化。
静态方法和实例方法有何不同?
静态方法和实例方法的区别主要体现在两个方面:
在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制
在一个静态方法内调用一个非静态成员为什么是非法的?
什么是方法的返回值?返回值的作用是什么?
什么是Java内部类?
内部类的分类有哪些
内部类可以分为四种:
成员内部类、
静态内部类、
局部内部类和
匿名内部类。
成员内部类
public class OuterClass {
private int outerField;
public class InnerClass {
private int innerField;
public InnerClass(int innerField) {
this.innerField = innerField;
}
public void printFields() {
System.out.println("outerField = " + outerField);
System.out.println("innerField = " + innerField);
}
}
public void testInnerClass() {
InnerClass innerClass = new InnerClass(10);
innerClass.printFields();
}
}
静态内部类
public class OuterClass {
private static int outerField;
public static class InnerClass {
private int innerField;
public InnerClass(int innerField) {
this.innerField = innerField;
}
public void printFields() {
System.out.println("outerField = " + outerField);
System.out.println("innerField = " + innerField);
}
}
public static void testInnerClass() {
InnerClass innerClass = new InnerClass(10);
innerClass.printFields();
}
}
局部内部类
public class OuterClass {
private int outerField;
public void testLocalInnerClass() {
final int localVariable = 10;
class LocalInnerClass {
private int innerField;
public LocalInnerClass(int innerField) {
this.innerField = innerField;
}
public void printFields() {
System.out.println("outerField = " + outerField);
System.out.println("localVariable = " + localVariable);
System.out.println("innerField = " + innerField);
}
}
LocalInnerClass localInnerClass = new LocalInnerClass(20);
localInnerClass.printFields();
}
}
匿名内部类
public interface MyInterface {
void doSomething();
}
public class OuterClass {
private int outerField;
public void testAnonymousInnerClass() {
MyInterface myInterface = new MyInterface() {
private int innerField;
@Override
public void doSomething() {
System.out.println("outerField = " + outerField);
System.out.println("innerField = " + innerField);
}
};
myInterface.doSomething();
}
}
内部类的优点
封装性:内部类可以访问外部类的私有成员,而外部类无法访问内部类的私有成员,从而实现了更好的封装性。
继承性:内部类可以继承其他类或者实现接口,从而实现更加灵活的程序设计。
回调函数:内部类可以作为回调函数的实现方式,从而实现更加灵活和复杂的事件处理。
代码组织:内部类可以将相关的代码组织在一起,从而提高代码的可读性和可维护性。
访问权限:内部类可以访问外部类的所有成员,包括私有成员,从而实现更加细粒度的访问权限控制。
内部类有哪些应用场景
局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final?
局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final呢?它内部原理是什么呢?先看这段代码:
public class Outer {
void outMethod(){
final int a =10;
class Inner {
void innerMethod(){
System.out.println(a);
}
}
}
}
以上例子,为什么要加final呢?是因为生命周期不一致, 局部变量直接存储在栈中,当方法执行结束后,非final的局部变量就被销毁。而局部内部类对局部变量的引用依然存在,如果局部内部类要调用局部变量时,就会出错。加了final,可以确保局部内部类使用的变量与外层的局部变量区分开,解决了这个问题。
public class Outer {
private int age = 12;
class Inner {
private int age = 13;
public void print() {
int age = 14;
System.out.println("局部变量:" + age);
System.out.println("内部类变量:" + this.age);
System.out.println("外部类变量:" + Outer.this.age);
}
}
public static void main(String[] args) {
Outer.Inner in = new Outer().new Inner();
in.print();
}
}
运行结果:
局部变量:14
内部类变量:13
外部类变量:12
构造器(constructor)是否可被重写(override)
重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分?
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。
重载:发生在同一个类中,方法名相同参数列表不同(参数类型不同、个数不同、顺序不同),与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分
重写:发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类(里氏代换原则);如果父类方法访问修饰符为private则子类中就不是重写。
重载是指在同一个类中定义多个方法,它们具有相同的名称但参数列表不同,可以根据传入的参数类型和数量来决定调用哪个方法。例如:
public void print(int num) {
System.out.println(num);
}
public void print(String str) {
System.out.println(str);
}
重写是指在子类中重新定义父类中已存在的方法,方法名、参数列表和返回类型都必须与父类中的方法相同,目的是为了实现多态。例如:
class Animal {
public void move() {
System.out.println("Animal is moving");
}
}
class Dog extends Animal {
@Override
public void move() {
System.out.println("Dog is moving");
}
}
== 和 equals 的区别是什么
== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数据类型 == 比较的是值,引用数据类型 == 比较的是内存地址)
equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。
情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。
举个例子:
String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
System.out.println(str1 == str2); // true,因为它们指向同一个字符串常量池中的对象
System.out.println(str1 == str3); // false,因为它们指向不同的对象
System.out.println(1 == 1.0); // true,因为它们的值相等
class Person {
private String name;
private int age;
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Person other = (Person) obj;
return name.equals(other.name) && age == other.age;
}
}
说明:
hashCode 与 equals (重要)
HashSet如何检查重复
两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?
hashCode和equals方法的关系
面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?”
hashCode()介绍
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode()函数。
散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)
为什么要有 hashCode
我们以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode:
hashCode()与equals()的相关规定
如果两个对象相等,则hashcode一定也是相同的
两个对象相等,对两个对象分别调用equals方法都返回true
两个对象有相同的hashcode值,它们也不一定是相等的
因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
对象的相等与指向他们的引用相等,两者有什么不同?
当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递
为什么 Java 中只有值传递
首先回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。 它用来描述各种程序设计语言(不只是Java)中方法参数传递方式。
Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。
public static void main(String[] args) {
int num1 = 10;
int num2 = 20;
swap(num1, num2);
System.out.println("num1 = " + num1);
System.out.println("num2 = " + num2);
}
public static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
System.out.println("a = " + a);
System.out.println("b = " + b);
}
结果:
a = 20 b = 10 num1 = 10 num2 = 20
解析:
通过上面例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样,
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4, 5 };
System.out.println(arr[0]);
change(arr);
System.out.println(arr[0]);
}
public static void change(int[] array) {
// 将数组的第一个元素变为0
array[0] = 0;
}
结果:
1 0
解析:
通过示例我们已经看到,实现一个改变对象参数状态的方法并不是一件难事。理由很简单,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。
总结
Java程序设计语言对对象采用的不是引用调用,实际上,对象引用是按值传递的。
下面再总结一下Java中方法参数的使用情况:
值传递和引用传递有什么区别
值传递:指的是在方法调用时,传递的参数是按值的拷贝传递,传递的是值的拷贝,也就是说传递后就互不相关了。
引用传递:指的是在方法调用时,传递的参数是按引用进行传递,其实传递的引用的地址,也就是变量所对应的内存空间的地址。传递的是值的引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间)。
JDK 中常用的包有哪些
java.lang
:提供 Java 语言的基本类和接口,如字符串、异常、线程等。java.util
:提供了 Java 中常用的工具类,如集合框架、日期和时间处理、随机数生成等。java.io
:提供了输入输出相关的类和接口,如文件读写、网络通信等。java.net
:提供了网络编程相关的类和接口,如 URL、Socket、ServerSocket 等。java.sql
:提供了 JDBC(Java 数据库连接)相关的类和接口,用于连接和操作数据库。java.awt
和 javax.swing
:提供了图形用户界面(GUI)相关的类和接口,如窗口、按钮、文本框等。java.math
:提供了高精度数学运算相关的类和接口,如 BigInteger、BigDecimal 等。字节流(Byte Stream):以字节为单位进行读写,通常用于读写二进制数据,如图像、音频、视频等。字节流的基类是 InputStream
和 OutputStream
。
字符流(Character Stream):以字符为单位进行读写,通常用于读写文本数据。字符流的基类是 Reader
和 Writer
。
字节缓冲流(Buffered Byte Stream):基于字节流,提供了缓冲功能,可以提高读写效率。字节缓冲流的基类是 BufferedInputStream
和 BufferedOutputStream
。
字符缓冲流(Buffered Character Stream):基于字符流,提供了缓冲功能,可以提高读写效率。字符缓冲流的基类是 BufferedReader
和 BufferedWriter
简答
详细回答
Socket
和 ServerSocket
相对应的 SocketChannel
和 ServerSocketChannel
两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发createDirectory(Path path)
:创建一个目录。createFile(Path path, FileAttribute<?>... attrs)
:创建一个文件。delete(Path path)
:删除一个文件或目录。exists(Path path, LinkOption... options)
:判断文件或目录是否存在。isDirectory(Path path, LinkOption... options)
:判断是否为目录。isExecutable(Path path)
:判断是否可执行。isHidden(Path path)
:判断是否为隐藏文件。isReadable(Path path)
:判断是否可读。isRegularFile(Path path, LinkOption... options)
:判断是否为普通文件。isSameFile(Path path, Path path2)
:判断两个路径是否指向同一文件。isWritable(Path path)
:判断是否可写。move(Path source, Path target, CopyOption... options)
:移动或重命名文件或目录。readAllBytes(Path path)
:读取文件的所有字节。readAllLines(Path path, Charset cs)
:读取文件的所有行。write(Path path, byte[] bytes, OpenOption... options)
:将字节数组写入文件。write(Path path, Iterable<? extends CharSequence> lines, Charset cs, OpenOption... options)
:将字符串序列写入文件。Java反射机制是指在运行时动态地获取类的信息并操作类的属性、方法和构造函数等。它允许程序在运行时通过名称来访问类的属性和方法,而不需要提前知道它们的名称。Java反射机制可以让我们在运行时动态地创建对象、调用方法、获取成员变量等操作,是Java语言的一个重要特性。
静态编译和动态编译
静态编译:在编译时确定类型,绑定对象
动态编译:运行时确定类型,绑定对象
Java反射机制的优点包括:
Java反射机制的缺点包括:
反射是框架设计的灵魂。
Spring框架使用反射机制来实现自动化配置。在Spring容器启动时,它会扫描应用程序中的所有类,并使用反射机制读取和设置这些类的注解信息。然后,它会根据这些注解信息来自动化配置应用程序的各个组件,如数据源、事务管理器、Web MVC等。
Hibernate框架使用反射机制来实现ORM映射。在Hibernate中,每个实体类都对应着一个数据库表。当Hibernate需要将一个实体对象存储到数据库中时,它会使用反射机制动态地读取这个实体对象的属性,并将这些属性映射到数据库表的列上。
Struts2框架使用反射机制来实现AOP编程。在Struts2中,开发人员可以通过注解或XML配置文件来定义拦截器,然后将这些拦截器应用到Action方法上。当请求到达Action方法时,Struts2会使用反射机制动态地代理这个方法,并将拦截器织入到这个方法中。
String str = "Hello, World!";
Class cls = str.getClass();
Class cls = String.class;
Class cls = Class.forName("java.lang.String");
字符型常量和字符串常量的区别
char c = 'A';
String str = "Hello, World!";
什么是字符串常量池?
String 是最基本的数据类型吗?
String有哪些特性?
String对象是不可变的,一旦创建了一个String对象,就不能再修改它的值。如果需要修改一个字符串,就必须创建一个新的String对象。
Java中的字符串常量池是在堆内存中的一个特殊区域,用于存储字符串常量。由于String对象是不可变的,因此可以在字符串常量池中共享相同的字符串对象,从而节省内存空间。
String对象可以使用equals()方法来比较两个字符串的内容是否相同。另外,String还实现了Comparable接口,因此可以使用compareTo()方法来比较两个字符串的大小关系。
String对象实现了CharSequence接口,因此可以像使用数组一样访问字符串中的每个字符。但与数组不同的是,String对象是不可变的,因此不能通过下标来修改字符串中的字符。
由于String对象是不可变的,因此可以被安全地共享。这使得Java能够在多个线程之间共享字符串常量池中的字符串对象,从而提高了程序的执行效率。
String为什么是不可变的吗?
简单来说就是String类利用了final修饰的char类型数组存储字符,源码如下图所以:
/** The value is used for character storage. */ private final char value[];
String真的是不可变的吗?
1 String不可变但不代表引用不可以变
String str = "Hello";
str = str + " World";
System.out.println("str=" + str);
结果:
str=Hello World
解析:
实际上,原来String的内容是不变的,只是str由原来指向"Hello"的内存地址转为指向"Hello World"的内存地址而已,也就是说多开辟了一块内存区域给"Hello World"字符串。
2.通过反射是可以修改所谓的“不可变”对象
// 创建字符串"Hello World", 并赋给引用s
String s = "Hello World";
System.out.println("s = " + s); // Hello World
// 获取String类中的value字段
Field valueFieldOfString = String.class.getDeclaredField("value");
// 改变value属性的访问权限
valueFieldOfString.setAccessible(true);
// 获取s对象上的value属性的值
char[] value = (char[]) valueFieldOfString.get(s);
// 改变value所引用的数组中的第5个字符
value[5] = '_';
System.out.println("s = " + s); // Hello_World
结果:
s = Hello World s = Hello_World
解析:
用反射可以访问私有成员, 然后反射出String对象中的value属性, 进而改变通过获得的value引用改变数组的结构。但是一般我们不会这么做,这里只是简单提一下有这个东西。
是否可以继承 String 类?
String str="i"与 String str=new String(“i”)一样吗?
String s = new String(“xyz”);创建了几个字符串对象?
两个对象,一个是静态区的"xyz",一个是用new创建在堆上的对象。
String str1 = "hello"; //str1指向静态区 String str2 = new String("hello"); //str2指向堆上的对象 String str3 = "hello"; String str4 = new String("hello"); System.out.println(str1.equals(str2)); //true System.out.println(str2.equals(str4)); //true System.out.println(str1 == str3); //true System.out.println(str1 == str2); //false System.out.println(str2 == str4); //false System.out.println(str2 == "hello"); //false str2 = str1; System.out.println(str2 == "hello"); //true
如何将字符串反转?
可以使用Java中的StringBuilder或StringBuffer类的reverse()方法来反转字符串。
示例代码:
String str = "Hello, World!";
StringBuilder sb = new StringBuilder(str);
sb.reverse();
String reversedStr = sb.toString();
System.out.println(reversedStr);
数组有没有 length()方法?String 有没有 length()方法
String 类的常用方法都有那些?
在使用 HashMap 的时候,用 String 做 key 有什么好处?
String和StringBuffer、StringBuilder的区别是什么?String为什么是不可变的?
可变性
线程安全性
性能
对于三者使用的总结
如果要操作少量的数据用 = String
单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
多线程操作字符串缓冲区 下操作大量数据 = StringBuffer
自动装箱与拆箱
装箱:将基本类型用它们对应的引用类型包装起来;
拆箱:将包装类型转换为基本数据类型;
int 和 Integer 有什么区别?
Java 是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java 为每一个基本数据类型都引入了对应的包装类型(wrapper class),int 的包装类就是 Integer,从 Java 5 开始引入了自动装箱/拆箱机制,使得二者可以相互转换。
Java 为每个原始类型提供了包装类型:
原始类型: boolean,char,byte,short,int,long,float,double
包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
Integer a= 127 与 Integer b = 127相等吗?
如果整型字面量的值在-128到127之间,那么自动装箱时不会new新的Integer对象,而是直接引用常量池中的Integer对象,超过范围 a1==b1的结果是false
public static void main(String[] args) {
Integer a = new Integer(3);
Integer b = 3; // 将3自动装箱成Integer类型
int c = 3;
System.out.println(a == b); // false 两个引用没有引用同一对象
System.out.println(a == c); // true a自动拆箱成int类型再和c比较
System.out.println(b == c); // true
Integer a1 = 128;
Integer b1 = 128;
System.out.println(a1 == b1); // false
Integer a2 = 127;
Integer b2 = 127;
System.out.println(a2 == b2); // true
}
博主总结不易 , 多点点关注和赞呀 !!!
文章浏览阅读1.1k次。一、选择题1. 串行接口是指( )。A. 接口与系统总线之间串行传送,接口与I/0设备之间串行传送B. 接口与系统总线之间串行传送,接口与1/0设备之间并行传送C. 接口与系统总线之间并行传送,接口与I/0设备之间串行传送D. 接口与系统总线之间并行传送,接口与I/0设备之间并行传送【答案】C2. 最容易造成很多小碎片的可变分区分配算法是( )。A. 首次适应算法B. 最佳适应算法..._874 计算机科学专业基础综合题型
文章浏览阅读9.7k次,点赞5次,收藏15次。连接xshell失败,报错如下图,怎么解决呢。1、通过ps -e|grep ssh命令判断是否安装ssh服务2、如果只有客户端安装了,服务器没有安装,则需要安装ssh服务器,命令:apt-get install openssh-server3、安装成功之后,启动ssh服务,命令:/etc/init.d/ssh start4、通过ps -e|grep ssh命令再次判断是否正确启动..._could not connect to '192.168.17.128' (port 22): connection failed.
文章浏览阅读209次。00000000_杰理 空白芯片 烧入key文件
文章浏览阅读475次。2023年初,“ChatGPT”一词在社交媒体上引起了热议,人们纷纷探讨它的本质和对社会的影响。就连央视新闻也对此进行了报道。作为新传专业的前沿人士,我们当然不能忽视这一热点。本文将全面解析ChatGPT,打开“技术黑箱”,探讨它对新闻与传播领域的影响。_引发对chatgpt兴趣的表述
文章浏览阅读259次。用Python数据分析方法进行汉字声调频率统计分析木合塔尔·沙地克;布合力齐姑丽·瓦斯力【期刊名称】《电脑知识与技术》【年(卷),期】2017(013)035【摘要】该文首先用Python程序,自动获取基本汉字字符集中的所有汉字,然后用汉字拼音转换工具pypinyin把所有汉字转换成拼音,最后根据所有汉字的拼音声调,统计并可视化拼音声调的占比.【总页数】2页(13-14)【关键词】数据分析;数据可..._汉字声调频率统计
文章浏览阅读64次。最近在做一个android系统移植的项目,所使用的开发板com1是调试串口,就是说会有uboot和kernel的调试信息打印在com1上(ttySAC0)。因为后期要使用ttySAC0作为上层应用通信串口,所以要把所有的调试信息都给去掉。参考网上的几篇文章,自己做了如下修改,终于把调试信息重定向到ttySAC1上了,在这做下记录。参考文章有:http://blog.csdn.net/longt..._嵌入式rootfs 输出重定向到/dev/console
文章浏览阅读1.2k次,点赞4次,收藏12次。1,先去iconfont登录,然后选择图标加入购物车 2,点击又上角车车添加进入项目我的项目中就会出现选择的图标 3,点击下载至本地,然后解压文件夹,然后切换到uniapp打开终端运行注:要保证自己电脑有安装node(没有安装node可以去官网下载Node.js 中文网)npm i -g iconfont-tools(mac用户失败的话在前面加个sudo,password就是自己的开机密码吧)4,终端切换到上面解压的文件夹里面,运行iconfont-tools 这些可以默认也可以自己命名(我是自己命名的_uniapp symbol图标
文章浏览阅读1.2w次,点赞25次,收藏192次。char*和char[]都是指针,指向第一个字符所在的地址,但char*是常量的指针,char[]是指针的常量_c++ char*
文章浏览阅读930次。代码编辑器或者文本编辑器,对于程序员来说,就像剑与战士一样,谁都想拥有一把可以随心驾驭且锋利无比的宝剑,而每一位程序员,同样会去追求最适合自己的强大、灵活的编辑器,相信你和我一样,都不会例外。我用过的编辑器不少,真不少~ 但却没有哪款让我特别心仪的,直到我遇到了 Sublime Text 2 !如果说“神器”是我能给予一款软件最高的评价,那么我很乐意为它封上这么一个称号。它小巧绿色且速度非
文章浏览阅读4.1k次。一、选择法这是每一个数出来跟后面所有的进行比较。2.冒泡排序法,是两个相邻的进行对比。_对十个数进行大小排序java
文章浏览阅读2.9k次。物联网开发笔记——使用网络调试助手连接阿里云物联网平台(基于MQTT协议)其实作者本意是使用4G模块来实现与阿里云物联网平台的连接过程,但是由于自己用的4G模块自身的限制,使得阿里云连接总是无法建立,已经联系客服返厂检修了,于是我在此使用网络调试助手来演示如何与阿里云物联网平台建立连接。一.准备工作1.MQTT协议说明文档(3.1.1版本)2.网络调试助手(可使用域名与服务器建立连接)PS:与阿里云建立连解释,最好使用域名来完成连接过程,而不是使用IP号。这里我跟阿里云的售后工程师咨询过,表示对应_网络调试助手连接阿里云连不上
文章浏览阅读544次,点赞5次,收藏6次。运算符与表达式任何高级程序设计语言中,表达式都是最基本的组成部分,可以说C++中的大部分语句都是由表达式构成的。_无c语言基础c++期末速成