基础部分
面向对象的特性
Java面向对象编程的特性包括:
-
封装(Encapsulation):将数据和操作数据的方法封装在一起,以避免外部的直接访问和修改,从而确保数据的安全性和一致性。
-
继承(Inheritance):通过继承机制,一个类可以从另一个类中继承属性和方法,从而减少代码的重复性,并且提高代码的可重用性和可维护性。
-
多态(Polymorphism):通过多态机制,一个对象可以具有多种形态,即同一个方法可以根据不同的对象调用出现不同的行为,从而提高代码的灵活性和可扩展性。
重载和重写的区别
重载(Overloading)和重写(Overriding)是Java中两个不同的概念。
重载指的是在同一个类中定义多个方法,它们具有相同的方法名,但参数列表不同(参数个数或类型不同),返回值可以相同也可以不同。在调用重载方法时,编译器会根据传入的参数类型和个数来确定调用哪个方法。
重写指的是子类重写父类的方法,方法名、参数列表和返回值都必须相同。当子类调用重写方法时,会优先调用子类中的方法,而不是父类中的方法。
接口和抽象类的区别
Java中的接口和抽象类都可以用来实现抽象,但它们有一些重要的区别:
- 接口中的所有方法都是抽象的,而抽象类可以有抽象方法和非抽象方法。
- 接口中的所有变量都是public static final的常量,而抽象类可以有各种类型的变量。
- 类可以实现多个接口,但只能继承一个抽象类。
- 接口中不能有构造方法,而抽象类可以有构造方法。
接口通常用于定义类之间的协议,而抽象类则用于实现类之间的共性。如果你需要在不同的类之间共享代码,那么你应该使用抽象类。如果你需要实现一组相关但不同的类,那么你应该使用接口。
equals和==的区别
在Java中,==和equals()都用于比较两个对象。但是它们之间有一些重要的区别:
- ==比较的是两个对象的引用,即它们是否指向同一个内存地址。而equals()比较的是两个对象的内容是否相同。
- ==是一个操作符,可以用于比较任何两个数据类型的变量,包括基本数据类型和对象类型。而equals()是一个方法,只能用于比较对象类型的变量。
- 对于基本数据类型,==比较的是它们的值是否相等。而对于对象类型,==比较的是它们的引用是否相等。而equals()方法可以根据需要来实现不同的比较方式。
下面是一个示例,演示了==和equals()的不同:
String str1 = "Hello";
String str2 = "Hello";
String str3 = new String("Hello");
// 使用==比较
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // false
// 使用equals()比较
System.out.println(str1.equals(str2)); // true
System.out.println(str1.equals(str3)); // true
在上面的示例中,我们使用了三个不同的字符串变量。str1和str2都指向同一个字符串对象,因此使用==比较时返回true。而str3指向一个新的字符串对象,因此使用==比较时返回false。但是,由于它们的内容都相同,因此使用equals()比较时返回true。
final关键字的作用
在Java中,final是一个关键字,用于表示“最终的”或“不可变的”。它可以用于变量、方法和类,具体的作用如下:
- 对于变量,final表示该变量只能被赋值一次,即它是一个常量。一旦被赋值,就不能再修改它的值。final变量必须在声明时或构造函数中初始化。
- 对于方法,final表示该方法不能被子类重写。这通常用于确保方法的行为不会被子类修改。
- 对于类,final表示该类不能被继承。这通常用于确保类的行为不会被子类修改。
stringbuilder和stringbuffer的区别
StringBuilder 和 StringBuffer 都是 Java 中用于处理字符串的类,它们的主要区别如下:
- 线程安全性:StringBuffer 是线程安全的,而 StringBuilder 不是线程安全的。也就是说,当多个线程同时访问 StringBuffer 对象时,不需要额外的同步措施,因为 StringBuffer 内部会自动进行同步。而当多个线程同时访问 StringBuilder 对象时,需要使用同步措施来保证线程安全。
- 性能:由于 StringBuffer 内部需要进行同步,因此在多线程环境下,它的性能会比 StringBuilder 差。在单线程环境下,StringBuilder 的性能会比 StringBuffer 更好。
- API:StringBuffer 和 StringBuilder 的 API 是相同的,它们都提供了 append、insert、delete、replace 等方法来操作字符串。。
总的来说,StringBuilder 和 StringBuffer 都是用于处理字符串的类,它们的主要区别在于线程安全性和性能方面。如果需要在多线程环境下操作字符串,应该使用 StringBuffer,否则建议使用 StringBuilder 来获得更好的性能
StringBuffer如何保证线程安全的
StringBuffer
是一个线程安全的可变字符串类,它的线程安全是通过在每个方法上加上 synchronized
关键字来实现的。
当一个线程调用 StringBuffer
对象的某个方法时,它会锁定这个对象,这样其他线程就不能同时调用这个对象的其他方法。这样就可以保证在同一时刻只有一个线程能够修改 StringBuffer
对象,从而避免了多线程并发修改同一个对象的问题。
需要注意的是,由于 StringBuffer
的每个方法都是同步的,因此在高并发的情况下,会影响程序的性能。如果不需要线程安全的可变字符串类,可以考虑使用 StringBuilder
,它的性能比 StringBuffer
更好,但不是线程安全的。
多线程部分
创建一个线程有哪些方法
在Java中,创建一个线程有以下几种方法:
- 继承Thread类:创建一个类并继承Thread类,重写run()方法,然后创建该类的对象并调用start()方法来启动线程。
class MyThread extends Thread { public void run() { // 线程执行的代码 } } MyThread t = new MyThread(); t.start();
- 实现Runnable接口:创建一个类实现Runnable接口,实现run()方法,然后创建该类的对象并将其作为参数传递给Thread类的构造方法来创建线程对象,最后调用start()方法启动线程。
class MyRunnable implements Runnable { public void run() { // 线程执行的代码 } } MyRunnable r = new MyRunnable(); Thread t = new Thread(r); t.start();
- Callable和Future接口:创建一个类实现Callable接口并实现call()方法,然后创建一个FutureTask对象并将其作为参数传递给Thread类的构造方法来创建线程对象,最后调用start()方法启动线程。
class MyCallable implements Callable<Integer> { public Integer call() throws Exception { // 线程执行的代码 return 0; } } MyCallable c = new MyCallable(); FutureTask<Integer> ft = new FutureTask<>(c); Thread t = new Thread(ft); t.start();
Runnable和Callable的区别
它们之间有以下几点区别:
-
返回值类型不同:Runnable的run()方法没有返回值,而Callable的call()方法有返回值。
-
异常处理不同:Runnable的run()方法不能抛出异常,而Callable的call()方法可以抛出异常。
-
使用方式不同:Runnable接口的实现类可以被多个线程共享,而Callable接口的实现类只能被一个线程执行。
-
获取线程执行结果的方式不同:Runnable接口的实现类没有提供获取线程执行结果的方法,而Callable接口的实现类可以通过Future接口的get()方法获取线程执行结果。
因此,如果需要在线程执行完毕后获取执行结果,可以使用Callable接口。如果只是简单地执行一些任务,可以使用Runnable接口。