八股系列——Java基础

如果有兴趣了解更多相关内容,欢迎来我的个人网站看看:瞳孔空间

1.Java 语言具有哪些特点?

  • Java 为纯面向对象的语言。它能够直接反应现实生活中的对象。
  • 具有平台无关性。Java 利用 Java 虚拟机运行字节码,无论是在 Windows、Linux 还是 MacOS 等其它平台对 Java 程序进行编译,编译后的程序可在其它平台运行。
  • Java 为解释型语言,编译器把 Java 代码编译成平台无关的中间代码,然后在 JVM 上解释运行,具有很好的可移植性。
  • Java 提供了很多内置类库。如对多线程支持,对网络通信支持,最重要的⼀点是提供了垃圾回收器。
  • Java 具有较好的安全性和健壮性。Java 提供了异常处理和垃圾回收机制,去除了 C++中难以理解的指针特性

2.简述 Java 基本数据类型

  • byte: 占用 1 个字节,取值范围-128 ~ 127
  • short: 占用 2 个字节,取值范围-215 ~ 215-1
  • int:占用 4 个字节,取值范围-231 ~ 231-1
  • long:占用 8 个字节
  • float:占用 4 个字节
  • double:占用 8 个字节
  • char: 占用 2 个字节
  • boolean:占用大小根据实现虚拟机不同有所差异

3.简述自动装箱拆箱

对于 Java 基本数据类型,均对应⼀个包装类。
装箱就是自动将基本数据类型转换为包装器类型,如 int->Integer
拆箱就是自动将包装器类型转换为基本数据类型,如 Integer->int

4.简述 Java 访问修饰符

  • default: 默认访问修饰符,在同⼀包内可见
  • private: 在同⼀类内可见,不能修饰类
  • protected : 对同⼀包内的类和所有子类可见,不能修饰类
  • public: 对所有类可见

5.构造方法、成员变量以及静态成员变量三者的初始化顺序

先后顺序:静态成员变量、成员变量、构造方法。

详细的先后顺序:
	⽗类静态变量
	⽗类静态代码块
	⼦类静态变量
	⼦类静态代码块
	⽗类⾮静态变量
	⽗类⾮静态代码块
	⽗类构造函数
	⼦类⾮静态变量
	⼦类⾮静态代码块
	⼦类构造函数

6.Java 代码块执行顺序

  • 父类静态代码块(只执行⼀次)
  • 子类静态代码块(只执行⼀次)
  • 父类构造代码块
  • 父类构造函数
  • 子类构造代码块
  • 子类构造函数
  • 普通代码块
代码块说明:

普通代码块:就是在方法后面使用"{}"括起来的代码片段,不能单独执行,必须调下其方法名才可以执行。
静态代码块:在类中使用static修饰,并使用"{}"括起来的代码片段,用于静态变量的初始化或对象创建前的环境初始化。
同步代码块:使用synchronize关键字修饰,并使用"{}"括起来的代码片段。它表示在同一时间只能有一个线程进入到该方法块中,是一种多线程保护机制。
构造代码块:在类中没与任何的前缀或后缀,并使用"{}"括起来的代码片段。

7.面向对象的三大特性

  • 封装:将客观事物抽象成类,每个类可以把自身数据和方法只让可信的类或对象操作,对不可信的进行信息隐藏。
  • 继承:对象的⼀个新类可以从现有的类中派生,派生类可以从它的基类那继承方法和实例变量,且派生类可以修改或新增新的方法使之更适合特殊的需求。
  • 多态:允许不同类的对象对同一消息作出响应。不同对象调用相同方法即使参数也相同,最终表现行为是不⼀样的。

8.为什么 Java 不支持多重继承

原因一:为了程序的结构能够更加清晰从而便于维护。
       
       假设 Java 语言⽀持多重继承,类 C 继承自类 A 和类 B,如果类 A 和 B 都有自定义的成员方法 f() ,
	   那么当代码中调用类 C 的 f() 会产⽣⼆义性。

       Java 语言通过实现多个接⼝间接支持多重继承。接口由于只包含⽅法定义,不能有方法的实现,类 C 继承
       接口 A 与接口 B 时即使它们都有方法 f() ,也不能直接调用方法,需实现具体的 f() 方法才能调⽤,不会产⽣⼆义性。

// TODO:类型转换?
原因二:多重继承会使类型转换、构造⽅法的调⽤顺序变得复杂,会影响到性能。

9.简述 Java 的多态

Java 多态可以分为编译时多态和运⾏时多态。

编译时多态主要指⽅法的重载,即通过参数列表的不同来区分不同的⽅法。

运⾏时多态主要指继承⽗类和实现接⼝时,可使⽤⽗类引⽤指向⼦类对象。

运⾏时多态的实现:主要依靠⽅法表,⽅法表中最先存放的是 Object 类的⽅法,接下来是该类的⽗类的⽅
法,最后是该类本身的⽅法。如果⼦类改写了⽗类的⽅法,那么⼦类和⽗类的那些同名⽅法共享⼀个⽅法表
项,都被认作是⽗类的⽅法。因此可以实现运⾏时多态。

运行时多态实现的前提
1、运用了继承或者实现
2、实现了方法的重写
3、使用了父类引用指向子类对象

多态的作用
在实际开发的过程中,使用父类类型作为方法形式参数,传递子类对象给方法,进行方法的调用,
可以实现使用一个方法对多种子类对象进行同一个操作,提高了程序的扩展性,也简化了代码的开发
(不再需要为了对多种子类对象实现同一个功能而编写多个函数)。

10.Java 提供的多态机制

Java 提供了两种用于多态的机制,分别是重载与覆盖。
重载:重载是指同⼀个类中有多个同名的⽅法,但这些方法有不同的参数,在编译期间就可以确定调用哪个方法。

覆盖:覆盖是指派生类重写基类的方法,使用基类指向其子类的实例对象,或接口的引用变量指向其实现类
的实例对象,在程序调用的运行期根据引用变量所指的具体实例对象调用正在运行的那个对象的方法,即需
要到运行期才能确定调用哪个方法。

11.接口和抽象类的相同点和不同点

相同点:

  • 都不能被实例化。
  • 接口的实现类或抽象类的子类需实现接口或抽象类中相应的方法才能被实例化。

不同点:

  • 接口只能有方法定义,不能有方法的实现,而抽象类可以有方法的定义与实现。
  • 实现接口的关键字为 implements,继承抽象类的关键字为 extends。⼀个类可以实现多个接口,只能继承⼀个抽象类。
  • 当子类和父类之间存在逻辑上的层次结构,推荐使用抽象类,有利于功能的累积。当功能不需要,希望支持差别较大的两个或更多对象间的特定交互行为,推荐使用接口。使用接口能降低软件系统的耦合度,便于日后维护或添加删除方法。

12.简述内部类及其作用

内部类是指在一个外部类的内部再定义一个类。内部类作为外部类的一个成员,并且依附于外部类而存在的。
内部类可为静态,可用protectedprivate修饰(而外部类只能使用public和缺省的包访问权限)。

内部类的共性:
1: 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,
   但是前面冠以外部类的类名和$符号。
2: 内部类不能用普通的方式访问。
3: 内部类声明成静态的,就不能随便的访问外部类的成员变量了,
   此时内部类只能访问外部类的静态成员变量。
4: 外部类不能直接访问内部类的的成员,但可以通过内部类对象来访问

为什么需要内部类
1: 内部类方法可以访问该类定义所在的作用域的数据,包括私有的数据
2: 内部类可以对同一个包中的其他类隐藏起来,一般的非内部类,
   是不允许有 privateprotected权限的,但内部类可以
3: 可以实现多重继承
4: 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷

成员内部类:
	作为成员对象的内部类。可以访问 private 及以上外部类的属性和⽅法。
    外部类想要访问内部类属性或⽅法时,必须要创建⼀个内部类对象,
    然后通过该对象访问内部类的属性或⽅法。外部类也可访问 private 修饰的内部类属性。

public class OutClass {
	private String name = " ";  // 成员属性
	
	public class InnerClass{  // 成员内部类
		private String name = "";  // 成员内部类中的属性可以和外部类的属性同名
		private static Integer age = 18;  // 此处报编译错误,成员内部类中的属性不可以用static修饰
		
		public static void print() {  // 内部类中的方法也不能用static修饰			
		}
	}
}

局部内部类:
	存在于⽅法中的内部类.
	不能使用访问控制修饰符和 static 修饰符修饰。
	局部内部类只在当前方法中有效。
	局部内部类中不能定义 static 成员。
	局部内部类中还可以包含内部类,但是这些内部类也不能使用访问控制修饰符和 static 修饰符修饰。
	在局部内部类中可以访问外部类的所有成员。
	在局部内部类中只可以访问当前方法中 final 类型的参数与变量。如果方法中的成员与外部类中的成员同名,则可以使用 <OuterClassName>.this.<MemberName> 的形式访问外部类中的成员。

public class Test {
    int a = 0;
    int d = 0;
    public void method() {
        int b = 0;
        final int c = 0;
        final int d = 10;
        class Inner {
            int a2 = a;    // 访问外部类中的成员
            // int b2 = b;    // 编译出错
            int c2 = c;    // 访问方法中的成员
            int d2 = d;    // 访问方法中的成员
            int d3 = Test.this.d;    //访问外部类中的成员
        }
        Inner i = new Inner();
        System.out.println(i.d2);    // 输出10
        System.out.println(i.d3);    // 输出0
    }
    public static void main(String[] args) {
        Test t = new Test();
        t.method();
    }
}

匿名内部类:
	匿名类是指没有类名的内部类,必须在创建时使用 new 语句来声明类。
	使用匿名类可使代码更加简洁、紧凑,模块化程度更高。
	匿名类和局部内部类一样,可以访问外部类的所有成员。
	如果匿名类位于一个方法中,则匿名类只能访问方法中 final 类型的局部变量和参数。
	匿名类中允许使用非静态代码块进行成员初始化操作。
	匿名类的非静态代码块会在父类的构造方法之后被执行。

public class Out {
    void show() {
        System.out.println("调用 Out 类的 show() 方法");
    }
}
public class TestAnonymousInterClass {
    // 在这个方法中构造一个匿名内部类
    private void show() {
        Out anonyInter = new Out() {
            // 获取匿名内部类的实例
            void show() {
                System.out.println("调用匿名类中的 show() 方法");
            }
        };
        anonyInter.show();
    }
    public static void main(String[] args) {
        TestAnonymousInterClass test = new TestAnonymousInterClass();
        test.show();  // 调用匿名类中的 show() 方法
    }
}

静态内部类:
	静态内部类是指使用 static 修饰的内部类
	在创建静态内部类的实例时,不需要创建外部类的实例。
	静态内部类中可以定义静态成员和实例成员。
	外部类以外的其他类需要通过完整的类名访问静态内部类中的静态成员,如果要访问静态内部类中的实例成员,则需要通过静态内部类的实例。
	静态内部类可以直接访问外部类的静态成员,如果要访问外部类的实例成员,则需要通过外部类的实例去访问。

public class Outer {
    static class Inner {
        int a = 0;    // 实例变量a
        static int b = 0;    // 静态变量 b
    }
}
class OtherClass {
    Outer.Inner oi = new Outer.Inner();
    int a2 = oi.a;    // 访问实例成员
    int b2 = Outer.Inner.b;    // 访问静态成员
}

13.Java 中关键字 static 的作用是什么

static 的主要作用有两个:

  • 为某种特定数据类型或对象分配与创建对象个数无关的单⼀的存储空间。
  • 使得某个方法或属性与类而不是对象关联在⼀起,即在不创建对象的情况下可通过类直接调用方法或使用类的属性。

具体而言 static 又可分为 4 种使用方式:

  • 修饰成员变量。用 static 关键字修饰的静态变量在内存中只有⼀个副本。只要静态变量所在的类被加载,这个静态变量就会被分配空间,可以使用“类.静态变量”和“对象.静态变量”的方法使用。
  • 修饰成员方法。static 修饰的方法无需创建对象就可以被调用。static 方法中不能使用 this 和 super 关键字,不能调用非 static 方法,只能访问所属类的静态成员变量和静态成员方法。
  • 修饰代码块。JVM 在加载类的时候会执行 static 代码块。static 代码块常用于初始化静态变量。static代码块只会被执行⼀次。
  • 修饰内部类。static 内部类可以不依赖外部类实例对象而被实例化。静态内部类不能与外部类有相同的名字,不能访问普通成员变量,只能访问外部类中的静态成员和静态成员方法。

14.为什么要把 String 设计为不可变

  • 节省空间:字符串常量池(String pool)是 Java 堆内存中一个特殊的存储区域,当创建一个 String 对象时,假如此字符串已经存在于常量池中,则不会创建新的对象,而是直接引用已经存在的对象。这样做能够减少 JVM 的内存开销,提高效率。
  • 多线程安全:多线程中,可变对象的值很可能被其他线程改变,造成不可预期的结果。而不可变的 String 可以自由在多个线程之间共享,不需要同步处理。
  • hashcode 缓存的需要:因为字符串不可变,所以在它创建的时候 hashcode 就被缓存了,不需要重新计算。这就使得字符串很适合作为 HashMap 中的 key,效率大大提高。

15.简述 String/StringBuffer 与 StringBuilder

  • String 类采用利用 final 修饰的字符数组进行字符串保存,因此不可变。如果对 String 类型对象修改,需要新建对象,将老字符和新增加的字符⼀并存进去。
  • StringBuilder,采用无 final 修饰的字符数组进行保存,因此可变。但线程不安全。相较于StringBuffer有速度优势,因此多数情况下建议使用 StringBuilder 类。
  • StringBuffer,采用无 final 修饰的字符数组进行保存,因此可变。所有公开方法都是 synchronized 修饰的,所以线程安全。在应用程序要求线程安全的情况下必须使用 StringBuffer 类。

16.判等运算符 == 与 equals 的区别

== 比较的是引⽤,equals 比较的是内容。

如果变量是基础数据类型,== 用于比较其对应值是否相等。如果变量指向的是对象,== 用于比较两个对象
是否指向同⼀块存储空间。

equals 是 Object 类提供的⽅法之⼀,每个 Java 类都继承自 Object 类,所以每个对象都具有 equals 这个
方法。Object 类中定义的 equals 方法内部是直接调用 == 比较对象的。但通过覆盖的方法可以让它不是比
较引用而是比较数据内容。

17.简述 Object 类常用方法

  • hashCode:通过对象计算出的散列码。用于 map 型或 equals 方法。需要保证同⼀个对象多次调用该方法,总返回相同的整型值。
  • equals:判断两个对象是否⼀致。需保证 equals 方法相同对应的对象 hashCode 也相同。
  • toString: 用字符串表示该对象
  • clone: 深拷贝⼀个对象

18.深拷贝与浅拷贝

首先可以了解一下引用拷贝与对象拷贝

引用拷贝:创建一个指向对象的引用变量的拷贝。

Test1 test = new Test1();
Test1 cloneTest = test;
System.out.println(test.hashCode());  // 1360875712
System.out.println(cloneTest.hashCode());  // 1360875712

对象拷贝:创建对象本身的一个副本。

// 需要实现Cloneable接口,否则会报错
public class Test1 implements Cloneable {
    @Override
    protected Test1 clone() throws CloneNotSupportedException {
        Test1 clone = null;
        try{
            clone = (Test1) super.clone();
        }catch(CloneNotSupportedException e){
            throw new RuntimeException(e); // won't happen
        }

        return clone;
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Test1 test = new Test1();
        Test1 cloneTest = (Test1)test.clone();
        // 输出结果可以看出,它们的地址是不同的,也就是说创建了新的对象
        // 而不是把原对象的地址赋给了一个新的引用变量,这就叫做对象拷贝。
        System.out.println(test.hashCode());  // 1360875712
        System.out.println(cloneTest.hashCode());  // 1625635731
    }
}

深拷贝和浅拷贝都是对象拷贝

浅拷贝:
	被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。
	即对象的浅拷贝会对“主”对象进行拷贝,但不会复制主对象里面的对象。
	"里面的对象“会在原来的对象和它的副本之间共享。
	简而言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象

public class Test1 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Teacher teacher = new Teacher();
        teacher.setName("老师");

        Student student1 = new Student();
        student1.setName("学生1");
        student1.setTeacher(teacher);

        Student student2 = (Student) student1.clone();
        System.out.println(student2.getName());  //  学生1
        System.out.println(student2.getTeacher().getName());  // 老师
        
        // 修改老师的信息
        teacher.setName("老师hhh");
        System.out.println(student1.getTeacher().getName());  // 老师hhh
        System.out.println(student2.getTeacher().getName());  // 老师hhh
    }
}

class Teacher implements Cloneable {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

class Student implements Cloneable{
    private String name;
    private Teacher teacher;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

深拷贝:
    深拷贝是一个整个独立的对象拷贝,深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。
    当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。
    简而言之,深拷贝把要复制的对象所引用的对象都复制了一遍。

public class Test1 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Teacher teacher = new Teacher();
        teacher.setName("老师");

        Student student1 = new Student();
        student1.setName("学生1");
        student1.setTeacher(teacher);

        Student student2 = (Student) student1.clone();
        System.out.println(student2.getName());  // 学生1
        System.out.println(student2.getTeacher().getName());  // 老师

        // 修改老师的信息
        teacher.setName("老师hhh");
        System.out.println(student1.getTeacher().getName());  // 老师hhh
        System.out.println(student2.getTeacher().getName());  // 老师
    }
}

class Teacher implements Cloneable {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Student implements Cloneable {
    private String name;
    private Teacher teacher;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        // 改为深复制:
        Student student = (Student) super.clone();
        // 本来是浅复制,现在将Teacher对象复制一份并重新set进来
        student.setTeacher((Teacher) student.getTeacher().clone());
        return student;
    }
}

19.简述Java异常的分类

Java 异常分为两种, 这两个类均继承 Throwable:
	Error(程序无法处理的错误)
	Exception(程序本身可以处理的异常)

Error 常见的有 StackOverFlowError、OutOfMemoryError 等等。

Exception 可分为运行时异常和非运行时异常。
	对于运行时异常,可以利用 try catch 的方式进行处理,也可以不处理。
	对于非运行时异常,必须处理,不处理的话程序无法通过编译。

20.简述 throw 与 throws 的区别

  • throw ⼀般是用在方法体的内部,由开发者定义当程序语句出现问题后主动抛出⼀个异常。
  • throws ⼀般用于方法声明上,代表该方法可能会抛出的异常列表。

21.出现在 Java 程序中的 finally 代码块是否⼀定会执行

当遇到下面情况不会执行。
	当程序在进入 try 语句块之前就出现异常时会直接结束。
	当程序在 try 块中强制退出时,如使用 System.exit(0),也不会执行 finally 块中的代码。

其它情况下,在 try/catch/finally 语句执行的时候,try 块先执行,当有异常发⽣,catch 和 finally 进行处理
后程序就结束了,当没有异常发生,在执行完 finally 中的代码后,后面代码会继续执行。

值得注意的是,当 try/catch 语句块中有 return 时,finally 语句块中的代码会在 return 之前执行。
如果 try/catch/finally 块中都有 return 语句,finally 块中的 return 语句会覆盖 try/catch 模块中的 return 语句。

22.final、finally 和 finalize 的区别是什么

  • final 用于声明属性、方法和类,分别表示属性不可变、方法不可覆盖、类不可继承。
  • finally 作为异常处理的⼀部分,只能在 try/catch 语句中使用,finally 附带⼀个语句块用来表示这个语句最终⼀定被执行,经常被用在需要释放资源的情况下。
  • finalize 是 Object 类的⼀个方法,在垃圾收集器执行的时候会调用被回收对象的 finalize()方法。当垃圾回收器准备好释放对象占用空间时,首先会调用 finalize()方法,并在下⼀次垃圾回收动作发生时真正回收对象占用的内存。

23.简述泛型

泛型,即“参数化类型”,解决不确定对象具体类型的问题。在编译阶段有效。在泛型使用过程中,操作的数据类型被指定为⼀个参数,这种参数类型在类中称为泛型类、接口中称为泛型接口和方法中称为泛型方法。

24.简述泛型擦除

Java 编译器生成的字节码是不包涵泛型信息的,泛型类型信息将在编译处理是被擦除,这个过程被称为泛型擦除。

25.序列化是什么

序列化是⼀种将对象转换成字节序列的过程,用于解决在对对象流进行读写操作时所引发的问题。序列化可以将对象的状态写在流里进行网络传输,或者保存到文件、数据库等系统里,并在需要的时候把该流读取出来重新构造成⼀个相同的对象。

26.简述 Java 序列化与反序列化的实现

  • 序列化:将 java 对象转化为字节序列,由此可以通过网络对象进行传输。
  • 反序列化:将字节序列转化为 java 对象。
  • 具体实现:实现 Serializable 接口,或实现 Externalizable 接口中的 writeExternal()与 readExternal()方法。

27.面向对象和面向过程的区别

  • 面向过程 :面向过程性能比面向对象高。 因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix 等⼀般采用面向过程开发。但是,面向过程没有面向对象易维护、易复用、易扩展。
  • 面向对象 :面向对象易维护、易复用、易扩展。 因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,面向对象性能比面向过程低
  • 注:Java 性能差的主要原因并不是因为它是面向对象语言,而是 Java 是半编译语言,最终的执行代码并不是可以直接被 CPU 执行的二进制机械码。而面向过程语言大多都是直接编译成机械码在电脑上执行,并且其它⼀些面向过程的脚本语言性能也并不⼀定比 Java 好。

28.Java 程序从源代码到运行

一般有两步:
	.java文件(源代码)	通过JDK中的javac编译成	.class文件(JVM可理解的Java字节码)
	.class文件	通过JVM转换成	机器可执行的二进制机器码

我们需要格外注意的是 .class 到 机器码 这⼀步。在这⼀步 JVM 类加载器⾸先加载字节码⽂件,然后
通过解释器逐行解释执行,这种方式的执行速度会相对比较慢。而且,有些方法和代码块是经常需要被
调⽤的(也就是所谓的热点代码),所以后面引进了 JIT 编译器,而 JIT 属于运行时编译。当 JIT 编
译器完成第⼀次编译后,其会将字节码对应的机器码保存下来,下次可以直接使⽤。而我们知道,机器
码的运⾏效率肯定是⾼于 Java 解释器的。这也解释了我们为什么经常会说 Java 是编译与解释共存的语⾔。

总结:Java 虚拟机(JVM)是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,
Linux,macOS),目的是使⽤相同的字节码,它们都会给出相同的结果。字节码和不同系统的 JVM 实
现是 Java 语⾔“⼀次编译,随处可以运行”的关键所在。

29.在 Java 中定义⼀个不做事且没有参数的构造方法的作用

Java 程序在执⾏⼦类的构造⽅法之前,如果没有⽤ super() 来调⽤⽗类特定的构造⽅法,则会调⽤
⽗类中“没有参数的构造⽅法”。因此,如果⽗类中只定义了有参数的构造⽅法,⽽在⼦类的构造⽅法中
⼜没有⽤ super() 来调⽤⽗类中特定的构造⽅法,则编译时将发⽣错误,因为 Java 程序在⽗类中找
不到没有参数的构造⽅法可供执⾏。解决办法是在⽗类⾥加上⼀个不做事且没有参数的构造⽅法。

30.简述线程、程序、进程的基本概念,以及他们之间关系

线程与进程相似,但线程是⼀个比进程更小的执行单位。⼀个进程在其执行的过程中可以产⽣多个线
程。与进程不同的是同类的多个线程共享同⼀块内存空间和⼀组系统资源,所以系统在产⽣⼀个线程,
或是在各个线程之间作切换⼯作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

程序是含有指令和数据的⽂件,被存储在磁盘或其他的数据存储设备中,也就是说程序是静态的代码。

进程是程序的⼀次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行⼀个程序即是
⼀个进程从创建,运行到消亡的过程。简单来说,⼀个进程就是⼀个执行中的程序,它在计算机中⼀个
指令接着⼀个指令地执行着,同时,每个进程还占有某些系统资源如 CPU 时间,内存空间,文件,输
入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程是进程划
分成的更小的运行单位。线程和进程最大的不同在于基本上各进程是独⽴的,而各线程则不⼀定,因为
同⼀进程中的线程极有可能会相互影响。从另⼀角度来说,进程属于操作系统的范畴,主要是同⼀段时
间内,可以同时执行⼀个以上的程序,而线程则是在同⼀程序内几乎同时执行⼀个以上的程序段。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值