1、面向对象的特征有哪些方面?
面向对象的特征主要有以下几个方面:
-
抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
例:
abstract class Animal { public abstract void makeSound(); // 抽象方法,让子类去实现 } class Dog extends Animal { @Override public void makeSound() { System.out.println("汪汪汪"); } } public class Main { public static void main(String[] args) { Animal animal = new Dog(); // 将Dog对象赋值给Animal类型的引用变量 animal.makeSound(); // 调用makeSound方法,输出“汪汪汪” } }
-
封装:封装是将对象的属性和方法包装在一起,对外提供一组简单的接口,隐藏内部实现细节,达到数据安全和简化编程的目的。
例:
class Person { private String name; // 私有属性,只能在类内部访问 public Person(String name) { // 构造方法,用于创建对象时初始化属性 this.name = name; } public String getName() { // getter方法,用于获取属性值 return name; } public void setName(String name) { // setter方法,用于设置属性值 this.name = name; } } public class Main { public static void main(String[] args) { Person person = new Person("张三"); // 创建一个Person对象 System.out.println(person.getName()); // 调用getName方法,输出“张三” person.setName("李四"); // 调用setName方法,将name属性设置为“李四” System.out.println(person.getName()); // 再次调用getName方法,输出“李四” } }
-
继承:继承是子类自动拥有父类所有属性和方法的过程,可以实现代码复用。
例:
class Animal { public void makeSound() { System.out.println("动物发出声音"); } } class Dog extends Animal { @Override public void makeSound() { System.out.println("汪汪汪"); } } public class Main { public static void main(String[] args) { Animal animal = new Dog(); // 创建一个Dog对象,并将其赋值给Animal类型的引用变量 animal.makeSound(); // 调用makeSound方法,输出“汪汪汪” } }
-
多态:多态是指不同类的对象对同一消息会作出不同的响应。
例:
interface Flyable { void fly(); // 定义一个飞行接口 } class Bird implements Flyable { @Override public void fly() { System.out.println("鸟儿展翅高飞"); } } class Airplane implements Flyable { @Override public void fly() { System.out.println("飞机在天空中翱翔"); } } public class Main { public static void main(String[] args) { Flyable bird = new Bird(); // 创建一个Bird对象,并将其赋值给Flyable类型的引用变量 Flyable airplane = new Airplane(); // 创建一个Airplane对象,并将其赋值给Flyable类型的引用变量 bird.fly(); // 调用bird对象的fly方法,输出“鸟儿展翅高飞” airplane.fly(); // 调用airplane对象的fly方法,输出“飞机在天空中翱翔” } }
2、访问修饰符 public,private,protected,以及不写(默认)时的区别?
public:公共访问修饰符,表示该成员可以被任何类访问。
private:私有访问修饰符,表示该成员只能被本类中的成员访问。
protected:受保护的访问修饰符,表示该成员可以被本类、子类和同一个包中的其他类访问。
default(不写):默认访问修饰符,表示该成员可以被同一包中的其他类访问。
例:
// 定义一个公共类
public class Person {
// 公共变量,可以被任何类访问
public String name;
// 私有变量,只能在本类中访问
private int age;
// 受保护的变量,可以被本类、子类和同一个包中的其他类访问
protected String gender;
// 默认访问修饰符,表示该成员可以被同一包中的其他类访问
String address;
}
// 定义一个子类
class Student extends Person {
// 可以访问父类的公共变量
public void setName(String name) {
this.name = name;
}
// 无法访问父类的私有变量
// private void setAge(int age) {
// this.age = age;
// }
// 可以访问父类的受保护的变量
protected void setGender(String gender) {
this.gender = gender;
}
// 可以访问父类的默认访问修饰符变量
void setAddress(String address) {
this.address = address;
}
}
3、String 是最基本的数据类型吗?
String 不是最基本的数据类型,它是 Java 中的一种引用数据类型。Java 中有八种基本数据类型:byte、short、int、long、float、double、char、boolean。
下面是一个使用基本数据类型的代码示例:
public class BasicDataTypes {
public static void main(String[] args) {
// 整型变量
int num1 = 10;
int num2 = 20;
int sum = num1 + num2;
System.out.println("整型相加结果:" + sum);
// 浮点型变量
double num3 = 3.14;
double num4 = 2.718;
double product = num3 * num4;
System.out.println("浮点型相乘结果:" + product);
// 字符型变量
char letter = 'A';
System.out.println("字符型变量值:" + letter);
// 布尔型变量
boolean flag = true;
System.out.println("布尔型变量值:" + flag);
}
}
输出结果:
整型相加结果:30
浮点型相乘结果:8.538
字符型变量值:A
布尔型变量值:true
4、float f=3.4;是否正确?
float f=3.4不是正确的。在Java中, 在Java中,浮点数默认为double类型,因此需要使用f后缀将其转换为float类型。下面是一个使用float类型的代码示例:
public class FloatExample {
public static void main(String[] args) {
// 声明一个float类型的变量
float f = 3.4f;
// 输出变量的值和类型
System.out.println("变量f的值为:" + f);
System.out.println("变量f的类型为:" + typeof(f));
}
}
输出结果:
变量f的值为:3.4
变量f的类型为:class java.lang.Float
5、short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?
short s1 = 1; s1 = s1 + 1; 这行代码是没有错误的,它的作用是将变量s1的值加1后重新赋值给s1。
short s1 = 1; s1 += 1; 这行代码也是没有错误的,它的作用是将变量s1的值加1后直接更新到原来的值上。
下面是带注释的代码示例:
// 定义一个short类型的变量s1,并初始化为1
short s1 = 1;
// 将s1的值加1,并将结果重新赋值给s1
s1 = s1 + 1;
// 将s1的值加1,并直接更新到原来的值上(等价于s1 = s1 + 1)
s1 += 1;
6、Java 有没有 goto?
Java 中没有 goto 语句。在 Java 中,goto 语句被认为是一种不良的编程习惯,因为它会导致代码难以理解和维护。相反,Java 提供了其他控制流语句,如 if、for、while 等,来控制程序的执行流程。
下面是一个使用 if 语句的示例代码,它实现了与 goto 语句相同的功能:
public class GotoExample {
public static void main(String[] args) {
int i = 0;
boolean flag = false;
while (!flag) {
if (i == 5) {
flag = true;
} else {
i++;
}
}
System.out.println("程序结束");
}
}
在上面的代码中,我们使用了一个 while 循环和一个 if 语句来模拟 goto 语句的行为。当 i 的值等于 5 时,我们将 flag 设置为 true,这将导致 while 循环结束。否则,我们将 i 的值加 1,并继续执行循环。
需要注意的是,虽然上面的代码实现了与 goto 语句相同的功能,但它并不是一个好的编程实践。在实际开发中,我们应该尽可能避免使用 goto 语句,而是使用更加清晰和易于维护的控制流语句来编写代码。
7、int 和 Integer 有什么区别?
int 和 Integer 都是 Java 中的基本数据类型,但它们之间有一些区别。
- int 是基本数据类型,而 Integer 是对象类型。
- int 是原始数据类型,它的值直接存储在内存中,而 Integer 是一个包装类,它的值存储在一个对象中。
- int 的取值范围是 -2^31 ~ 2^31-1,而 Integer 的取值范围是 -2^31 ~ 2^31-1 之间。
- int 是值传递,而 Integer 是引用传递。
下面是一个简单的示例代码,演示了 int 和 Integer 的区别:
public class IntAndIntegerExample {
public static void main(String[] args) {
// 定义一个 int 类型的变量 i,并赋值为 10
int i = 10;
System.out.println("i 的值:" + i); // 输出 i 的值:10
// 定义一个 Integer 类型的变量 j,并赋值为 null
Integer j = null;
System.out.println("j 的值:" + j); // 输出 j 的值:null
// 将 i 的值赋给 j
j = i;
System.out.println("j 的值:" + j); // 输出 j 的值:10
// 修改 j 的值
j = 20;
System.out.println("j 的值:" + j); // 输出 j 的值:20
}
}
在上面的代码中,我们定义了一个 int 类型的变量 i,并将其赋值为 10。然后,我们定义了一个 Integer 类型的变量 j,并将其赋值为 null。接下来,我们将 i 的值赋给了 j,并输出了 j 的值。最后,我们修改了 j 的值,并再次输出了 j 的值。
需要注意的是,由于 Integer 是一个对象类型,因此在将 int 类型的值赋给 Integer 类型的变量时,需要进行拆箱操作。而在将 Integer 类型的值赋给 int 类型的变量时,则不需要进行拆箱操作。
8、和&&的区别?
& 和 && 都是用于逻辑运算的运算符,但它们之间有一些区别。
- & 是按位与运算符,它可以用于整数类型和布尔类型。当用于整数类型时,它会将两个操作数进行按位与运算;当用于布尔类型时,它会将两个操作数进行逻辑与运算。
- && 是短路与运算符,它只能用于布尔类型。当第一个操作数的值为 false 时,它不会计算第二个操作数的值,因为无论第二个操作数的值是什么,结果都是 false。
下面是一个简单的示例代码,演示了 & 和 && 的区别:
public class AndOperatorExample {
public static void main(String[] args) {
// 定义两个整数变量 a 和 b,并初始化为 5 和 3
int a = 5;
int b = 3;
System.out.println("a & b 的结果是:" + (a & b)); // 输出 a & b 的结果是:1
// 定义两个布尔变量 c 和 d,并初始化为 true 和 false
boolean c = true;
boolean d = false;
System.out.println("c && d 的结果是:" + (c && d)); // 输出 c && d 的结果是:false
// 定义一个布尔变量 e,并初始化为 true
boolean e = true;
System.out.println("e & d 的结果是:" + (e & d)); // 输出 e & d 的结果是:false
}
}
在上面的代码中,我们分别使用了 & 和 && 运算符对整数和布尔类型的变量进行了逻辑运算。可以看到,当使用 & 运算符时,我们需要将整数类型的变量转换为布尔类型,然后再进行逻辑与运算;而当使用 && 运算符时,我们可以直接进行逻辑与运算,不需要进行类型转换。
9、解释内存中的栈(stack)、堆(heap)和方法区(method area)的用法
栈(stack)、堆(heap)和方法区(method area)是Java虚拟机中用于存储数据的三个主要部分。它们的作用和用法如下:
- 栈(stack)
栈是一种线性数据结构,它用于存储局部变量、方法参数、返回值等。每当一个方法被调用时,Java虚拟机会在栈上创建一个栈帧(frame)来存储该方法的相关信息。每个栈帧包含了方法的局部变量表、操作数栈、动态链接和方法出口等信息。当方法执行完毕后,对应的栈帧会被销毁。
下面是一个简单的示例代码,演示了栈的用法:
public class StackExample {
public static void main(String[] args) {
// 创建一个整数变量并赋值为10
int num = 10;
// 调用方法,将num作为参数传入
System.out.println("num的值是:" + add(num));
}
public static int add(int a) {
// 创建一个整数变量并赋值为20
int b = 20;
// 计算a和b的和,并将结果返回
return a + b;
}
}
在上面的代码中,我们定义了一个add方法,该方法接收一个整数参数a,并返回a和b的和。在main方法中,我们创建了一个整数变量num,并将其作为参数传入add方法。当add方法被调用时,Java虚拟机会在栈上创建一个栈帧来存储add方法的局部变量表、操作数栈等信息。当add方法执行完毕后,对应的栈帧会被销毁。
- 堆(heap)
堆是一种动态分配的内存区域,它用于存储对象实例。当我们创建一个对象时,Java虚拟机会在堆上为其分配一块连续的内存空间,并将该对象的引用存储在栈帧中。当我们需要访问该对象时,可以通过栈帧中的引用来访问堆上的内存空间。
下面是一个简单的示例代码,演示了堆的用法:
public class HeapExample {
public static void main(String[] args) {
// 创建一个字符串对象,并将其引用存储在栈帧中
String str = new String("Hello, world!");
// 输出字符串的内容
System.out.println(str);
}
}
在上面的代码中,我们创建了一个字符串对象,并将其引用存储在栈帧中。当str被访问时,Java虚拟机会通过栈帧中的引用来访问堆上的内存空间,并输出字符串的内容。
- 方法区(method area)
方法区是一种专门用于存储类信息、常量、静态变量等数据的区域。它与堆一样,也是在Java虚拟机启动时由JVM自动管理的。方法区的大小可以通过-Xmx和-Xms参数进行设置。
下面是一个简单的示例代码,演示了方法区的用法:
public class MethodAreaExample {
public static void main(String[] args) {
// 获取当前类的Class对象
Class<?> clazz = HeapExample.class;
// 获取clazz的方法名数组
String[] methodNames = clazz.getDeclaredMethods();
// 遍历方法名数组,并输出每个方法的名称
for (String methodName : methodNames) {
System.out.println(methodName);
}
}
}
在上面的代码中,我们首先获取了HeapExample类的Class对象,然后通过getDeclaredMethods()方法获取了该类的所有方法名数组。最后,我们遍历了方法名数组,并输出了每个方法的名称。需要注意的是,由于方法区是与类相关的,因此只有在同一个类中才能访问到方法区中的数据。
10、Math.round(11.5) 等于多少?Math.round(-11.5)等于多少?
Math.round() 是 JavaScript 中的一个内置函数,用于将一个数字四舍五入到最接近的整数。如果省略第二个参数,则默认为0。
对于第一个问题,Math.round(11.5) 的结果为 12,因为 11.5 四舍五入后最接近的整数是 12。
对于第二个问题,Math.round(-11.5) 的结果为 -11,因为 -11.5 四舍五入后最接近的整数是 -12,但由于 Math.round() 函数遵循“四舍六入五成双”的规则,即当小数部分为 5 时,会舍入到最近的偶数,因此最终结果为 -11。
下面是代码解释和注释:
// 使用 Math.round() 函数将 11.5 四舍五入到最接近的整数
var result1 = Math.round(11.5); // 结果为 12
// 使用 Math.round() 函数将 -11.5 四舍五入到最接近的整数
var result2 = Math.round(-11.5); // 结果为 -11,因为 Math.round() 函数遵循“四舍六入五成双”的规则
switch 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 String 上?
switch 语句可以作用在 byte、long 和 String 类型上。
对于 byte 类型,switch 语句会根据 byte 的值进行匹配,并执行相应的 case 分支。例如:
byte b = 1;
switch (b) {
case 1:
System.out.println("b 的值为 1");
break;
case 2:
System.out.println("b 的值为 2");
break;
default:
System.out.println("b 的值不是 1 或 2");
}
对于 long 类型,switch 语句同样会根据 long 的值进行匹配,并执行相应的 case 分支。例如:
long l = 3L;
switch (l) {
case 1L:
System.out.println("l 的值为 1");
break;
case 2L:
System.out.println("l 的值为 2");
break;
default:
System.out.println("l 的值不是 1 或 2");
}
对于 String 类型,switch 语句会根据字符串的内容进行匹配,并执行相应的 case 分支。例如:
String str = "hello";
switch (str) {
case "world":
System.out.println("str 的值为 world");
break;
case "hello":
System.out.println("str 的值为 hello");
break;
default:
System.out.println("str 的值不是 world 或 hello");
}
需要注意的是,如果 switch 语句中的值与任何 case 都不匹配,则会执行 default 分支。如果没有 default 分支,且没有任何 case 匹配,则会出现编译错误。