| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> Java知识库 -> 对Java的梳理和汇总 -> 正文阅读 |
|
[Java知识库]对Java的梳理和汇总 |
文章目录
前言粗糙的学完一遍Java基础后,发现对一些概念还有细节方面还是没有成体系,希望通过问题,过一遍基础的知识。问题包含《Java从入门到精通》中的重点问题。 一、Java(一)问答版1.1 为什么Java代码可以实现一次编写、到处运行?参考答案 JVM(Java虚拟机)是Java跨平台的关键。 在程序运行前,Java源代码(.java)需要经过编译器编译成字节码(.class)。在程序运行时,JVM负责将字节码翻译成特定平台下的机器码并运行,也就是说,只要在不同的平台上安装对应的JVM,就可以运行字节码文件。 同一份Java源代码在不同的平台上运行,它不需要做任何的改变,并且只需要编译一次。而编译好的字节码,是通过JVM这个中间的“桥梁”实现跨平台的,JVM是与平台相关的软件,它能将统一的字节码翻译成该平台的机器码。 1.2 一个Java文件里可以有多个类吗(不含内部类)?参考答案 一个java文件里可以有多个类,但最多只能有一个被public修饰的类; 如果这个java文件中包含public修饰的类,则这个类的名称必须和java文件名一致。 1.3 说一说你对Java访问权限的了解参考答案 Java语言为我们提供了三种访问修饰符,即private、protected、public,在使用这些修饰符修饰目标时,一共可以形成四种访问权限,即private、default、protected、public,注意在不加任何修饰符时为default访问权限。 在修饰成员变量/成员方法时,该成员的四种访问权限的含义如下: private:该成员可以被该类内部成员访问; default:该成员可以被该类内部成员访问,也可以被同一包下其他的类访问; protected:该成员可以被该类内部成员访问,也可以被同一包下其他的类访问,还可以被它的子类访问; public:该成员可以被任意包下,任意类的成员进行访问。 在**修饰类时,该类只有两种访问权限,**对应的访问权限的含义如下: default:该类可以被同一包下其他的类访问; public:该类可以被任意包下,任意的类所访问。 1.4 介绍一下Java的数据类型参考答案 Java数据类型包括基本数据类型和引用数据类型两大类。 基本数据类型有8个(String不是数据类型),可以分为4个小类,分别是整数类型(byte/short/int/long)、浮点类型(float/double)、字符类型(char)、布尔类型(boolean)。其中,4个整数类型中,int类型最为常用。2个浮点类型中,double最为常用。另外,在这8个基本类型当中,除了布尔类型之外的其他7个类型,都可以看做是数字类型,它们相互之间可以进行类型转换。 引用类型就是对一个对象的引用,根据引用对象类型的不同,可以将引用类型分为3类,即数组、类、接口类型。 引用类型本质上就是通过指针,指向堆中对象所持有的内存空间,只是Java语言不再沿用指针这个说法而已。 扩展阅读 对于基本数据类型,你需要了解每种类型所占据的内存空间,面试官可能会追问这类问题: byte:1字节(8位),数据范围是 -2^7 ~ 2^7-1。 short:2字节(16位),数据范围是 -2^15 ~ 2^15-1。 int:4字节(32位),数据范围是 -2^31 ~ 2^31-1。 long:8字节(64位),数据范围是 -2^63 ~ 2^63-1。 float:4字节(32位),数据范围大约是 -3.410^38 ~ 3.410^38。 double:8字节(64位),数据范围大约是 -1.810^308 ~ 1.810^308。 char:2字节(16位),数据范围是 \u0000 ~ \uffff。 boolean:Java规范没有明确的规定,不同的JVM有不同的实现机制。 对于引用数据类型,你需要了解JVM的内存分布情况,知道引用以及引用对象存放的位置,详见JVM部分的题目。 1.5 int类型的数据范围是多少?参考答案 int类型占4字节(32位),数据范围是 -2^31 ~ 2^31-1。 1.6 请介绍全局变量和局部变量的区别参考答案 Java中的变量分为成员变量和局部变量,它们的区别如下: 成员变量: 成员变量是在类的范围里定义的变量; 成员变量有默认初始值; 未被static修饰的成员变量也叫实例变量,它存储于对象所在的堆内存中,生命周期与对象相同; 被static修饰的成员变量也叫类变量,它存储于方法区中,生命周期与当前类相同。 局部变量: 局部变量是在方法里定义的变量; 局部变量没有默认初始值; 局部变量存储于栈内存中,作用的范围结束,变量空间会自动的释放。 注意事项 Java中没有真正的全局变量,面试官应该是出于其他语言的习惯说全局变量的,他的本意应该是指成员变量。 1.7 请介绍一下实例变量的默认值参考答案 实例变量若为引用数据类型,其默认值一律为null。若为基本数据类型,其默认值如下: byte:0 short:0 int:0 long:0L float:0.0F double:0.0 char:‘\u0000’ boolean:false 注意事项 上述默认值规则适用于所有的成员变量,所以对于类变量也是适用的。 1.8 如何对Integer和Double类型判断相等?参考答案 Integer、Double不能直接进行比较,这包括: 不能用==进行直接比较,因为它们是不同的数据类型; 不能转为字符串进行比较,因为转为字符串后,浮点值带小数点,整数值不带,这样它们永远都不相等; 不能使用compareTo方法进行比较,虽然它们都有compareTo方法,但该方法只能对相同类型进行比较。 整数、浮点类型的包装类,都继承于Number类型,而Number类型分别定义了将数字转换为byte、short、int、long、float、double的方法。所以,可以将Integer、Double先转为转换为相同的基本数据类型(如double),然后使用==进行比较。 示例代码 1 二、Java(二)1.9 面向对象的三大特征是什么?参考答案 面向对象的程序设计方法具有三个基本特征:封装、继承、多态。其中,封装指的是将对象的实现细节隐藏起来,然后通过一些公用方法来暴露该对象的功能;继承是面向对象实现软件复用的重要手段,当子类继承父类后,子类作为一种特殊的父类,将直接获得父类的属性和方法;多态指的是子类对象可以直接赋给父类变量,但运行时依然表现出子类的行为特征,这意味着同一个类型的对象在执行同一个方法时,可能表现出多种行为特征。 扩展阅读 抽象也是面向对象的重要部分,抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。**抽象并不打算了解全部问题,而只是考虑部分问题。**例如,需要考察Person对象时,不可能在程序中把Person的所有细节都定义出来,通常只能定义Person的部分数据、部分行为特征,而这些数据、行为特征是软件系统所关心的部分。 1.10 封装的目的是什么,为什么要有封装?参考答案 封装是面向对象编程语言对客观世界的模拟,在客观世界里,对象的状态信息都被隐藏在对象内部,外界无法直接操作和修改。对一个类或对象实现良好的封装,可以实现以下目的: 隐藏类的实现细节; 让使用者只能通过事先预定的方法来访问数据,从而可以在该方法里加入控制逻辑,限制对成员变量的不合理访问; 可进行数据检查,从而有利于保证对象信息的完整性; 便于修改,提高代码的可维护性。 扩展阅读 为了实现良好的封装,需要从两个方面考虑: 将对象的成员变量和实现细节隐藏起来,不允许外部直接访问; 把方法暴露出来,让方法来控制对这些成员变量进行安全的访问和操作。 封装实际上有两个方面的含义:把该隐藏的隐藏起来,把该暴露的暴露出来。 这两个方面都需要通过使用Java提供的访问控制符来实现。 1.11 说一说你对多态的理解参考答案 因为子类其实是一种特殊的父类,因此Java允许把一个子类对象直接赋给一个父类引用变量,无须任何类型转换,或者被称为向上转型,向上转型由系统自动完成。 当把一个子类对象直接赋给父类引用变量时,例如 BaseClass obj = new SubClass();,这个obj引用变量的编译时类型是BaseClass,而运行时类型是SubClass,当运行时调用该引用变量的方法时,其方法行为总是表现出子类方法的行为特征,而不是父类方法的行为特征,这就可能出现:相同类型的变量、调用同一个方法时呈现出多种不同的行为特征,这就是多态。 扩展阅读 多态可以提高程序的可扩展性,在设计程序时让代码更加简洁而优雅。 例如我要设计一个司机类,他可以开轿车、巴士、卡车等等,示例代码如下: 1 但对于程序的开发者来说,这显得繁琐,因为实际上这个司机可以驾驶更多的交通工具。当系统需要为这个司机增加车型时,开发者就需要相应的增加driver方法,类似的代码会堆积的越来越多,显得臃肿。 采用多态的方式来设计上述程序,就会变得简洁很多。我们可以为所有的交通工具定义一个父类Vehicle,然后按照如下的方式设计drive方法。调用时,我们可以传入Vehicle类型的实例,也可以传入任意的Vehicle子类型的实例,对于调用者来说一样的方便,但对于开发者来说,代码却变得十分的简洁了。 1 1.12 Java中的多态是怎么实现的?参考答案 多态的实现离不开继承,在设计程序时,我们可以将参数的类型定义为父类型。在调用程序时,则可以根据实际情况,传入该父类型的某个子类型的实例,这样就实现了多态。对于父类型,可以有三种形式,即普通的类、抽象类、接口。对于子类型,则要根据它自身的特征,重写父类的某些方法,或实现抽象类/接口的某些抽象方法。 1.13 Java为什么是单继承,为什么不能多继承?参考答案 首先,Java是单继承的,指的是Java中一个类只能有一个直接的父类。Java不能多继承,则是说Java中一个类不能直接继承多个父类。 其次,Java在设计时借鉴了C++的语法,而C++是支持多继承的。Java语言之所以摒弃了多继承的这项特征,是因为多继承容易产生混淆。比如,两个父类中包含相同的方法时,子类在调用该方法或重写该方法时就会迷惑。 准确来说,Java是可以实现"多继承"的。因为尽管一个类只能有一个直接父类,但是却可以有任意多个间接的父类。这样的设计方式,避免了多继承时所产生的混淆。 1.14 说一说重写与重载的区别参考答案 重载发生在同一个类中,若多个方法之间方法名相同、参数列表不同,则它们构成重载的关系。重载与方法的返回值以及访问修饰符无关,即重载的方法不能根据返回类型进行区分。 重写发生在父类子类中,若子类方法想要和父类方法构成重写关系,则它的方法名、参数列表必须与父类方法相同。另外,返回值要小于等于父类方法,抛出的异常要小于等于父类方法,访问修饰符则要大于等于父类方法。还有,若父类方法的访问修饰符为private,则子类不能对其重写。 1.15 构造方法能不能重写?参考答案 **构造方法不能重写。因为构造方法需要和类保持同名,而重写的要求是子类方法要和父类方法保持同名。**如果允许重写构造方法的话,那么子类中将会存在与类名不同的构造方法,这与构造方法的要求是矛盾的。 1.16 说一说hashCode()和equals()的关系参考答案 hashCode()用于获取哈希码(散列码),eauqls()用于比较两个对象是否相等,它们应遵守如下规定: 如果两个对象相等,则它们必须有相同的哈希码。 如果两个对象有相同的哈希码,则它们未必相等。 扩展阅读 在Java中,Set接口代表无序的、元素不可重复的集合,HashSet则是Set接口的典型实现。 当向HashSet中加入一个元素时,它需要判断集合中是否已经包含了这个元素,从而避免重复存储。由于这个判断十分的频繁,所以要讲求效率,绝不能采用遍历集合逐个元素进行比较的方式。实际上,HashSet是通过获取对象的哈希码,以及调用对象的equals()方法来解决这个判断问题的。 HashSet首先会调用对象的hashCode()方法获取其哈希码,并通过哈希码确定该对象在集合中存放的位置。假设这个位置之前已经存了一个对象,则HashSet会调用equals()对两个对象进行比较。若相等则说明对象重复,此时不会保存新加的对象。若不等说明对象不重复,但是它们存储的位置发生了碰撞,此时HashSet会采用链式结构在同一位置保存多个对象,即将新加对象链接到原来对象的之后。之后,再有新添加对象也映射到这个位置时,就需要与这个位置中所有的对象进行equals()比较,若均不相等则将其链到最后一个对象之后。 怎么这么多啊啊 1.17 为什么要重写hashCode()和equals()?参考答案 Object类提供的equals()方法默认是用 == 来进行比较的,也就是说只有两个对象是同一个对象时,才能返回相等的结果。而实际的业务中,我们通常的需求是,若两个不同的对象它们的内容是相同的,就认为它们相等。鉴于这种情况,Object类中equals()方法的默认实现是没有实用价值的,所以通常都要重写。 1.18**==和equals()有什么区别?**参考答案 ==运算符: 作用于基本数据类型时,是比较两个数值是否相等; 作用于引用数据类型时,是比较两个对象的内存地址是否相同,即判断它们是否为同一个对象; equals()方法: 没有重写时,Object默认以 == 来实现,即比较两个对象的内存地址是否相同; 进行重写后,一般会按照对象的内容来进行比较,若两个对象内容相同则认为对象相等,否则认为对象不等。 1.19 说一说String和StringBuffer有什么区别参考答案 String类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。 StringBuffer对象则代表一个字符序列可变的字符串,当一个StringBuffer被创建以后,通过StringBuffer提供的append()、insert()、reverse()、setCharAt()、setLength()等方法可以改变这个字符串对象的字符序列。一旦通过StringBuffer生成了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象。 1.20说一说StringBuffer和StringBuilder有什么区别参考答案 StringBuffer、StringBuilder都代表可变的字符串对象,它们有共同的父类 AbstractStringBuilder,并且两个类的构造方法和成员方法也基本相同。不同的是,StringBuffer是线程安全的,而StringBuilder是非线程安全的,所以StringBuilder性能略高。一般情况下,要创建一个内容可变的字符串,建议优先考虑StringBuilder类。 1.21 使用字符串时,new和""推荐使用哪种方式?参考答案 先看看 “hello” 和 new String(“hello”) 的区别: 当Java程序直接使用 “hello” 的字符串直接量时,JVM将会使用常量池来管理这个字符串; 当使用 new String(“hello”) 时,JVM会先使用常量池来管理 “hello” 直接量,再调用String类的构造器来创建一个新的String对象,新创建的String对象被保存在堆内存中。 显然,采用new的方式会多创建一个对象出来,会占用更多的内存,所以一般建议使用直接量的方式创建字符串。 1.22 说一说你对字符串拼接的理解参考答案 拼接字符串有很多种方式,其中最常用的有4种,下面列举了这4种方式各自适合的场景。
StringBuilder:如果拼接的字符串中包含变量,并不要求线程安全,则适合使用StringBuilder; StringBuffer:如果拼接的字符串中包含变量,并且要求线程安全,则适合使用StringBuffer; String类的concat方法:如果只是对两个字符串进行拼接,并且包含变量,则适合使用concat方法; 扩展阅读 采用 + 运算符拼接字符串时: 如果拼接的都是字符串直接量,则在编译时编译器会将其直接优化为一个完整的字符串,和你直接写一个完整的字符串是一样的,所以效率非常的高。 如果拼接的字符串中包含变量,则在编译时编译器采用StringBuilder对其进行优化,即自动创建StringBuilder实例并调用其append()方法,将这些字符串拼接在一起,效率也很高。但如果这个拼接操作是在循环中进行的,那么每次循环编译器都会创建一个StringBuilder实例,再去拼接字符串,相当于执行了 new StringBuilder().append(str),所以此时效率很低。 采用StringBuilder/StringBuffer拼接字符串时: StringBuilder/StringBuffer都有字符串缓冲区,缓冲区的容量在创建对象时确定,并且默认为16。当拼接的字符串超过缓冲区的容量时,会触发缓冲区的扩容机制,即缓冲区加倍。 缓冲区频繁的扩容会降低拼接的性能,所以如果能提前预估最终字符串的长度,则建议在创建可变字符串对象时,放弃使用默认的容量,可以指定缓冲区的容量为预估的字符串的长度。 采用String类的concat方法拼接字符串时: concat方法的拼接逻辑是,先创建一个足以容纳待拼接的两个字符串的字节数组,然后先后将两个字符串拼到这个数组里,最后将此数组转换为字符串。 在拼接大量字符串的时候,concat方法的效率低于StringBuilder。但是只拼接2个字符串时,concat方法的效率要优于StringBuilder。并且这种拼接方式代码简洁,所以只拼2个字符串时建议优先选择concat方法。 1.23 两个字符串相加的底层是如何实现的?参考答案 如果拼接的都是字符串直接量,则在编译时编译器会将其直接优化为一个完整的字符串,和你直接写一个完整的字符串是一样的。 如果拼接的字符串中包含变量,则在编译时编译器采用StringBuilder对其进行优化,即自动创建StringBuilder实例并调用其append()方法,将这些字符串拼接在一起。 1.24 String a = “abc”; ,说一下这个过程会创建什么,放在哪里?参考答案 JVM会使用常量池来管理字符串直接量。在执行这句话时,JVM会先检查常量池中是否已经存有"abc",若没有则将"abc"存入常量池,否则就复用常量池中已有的"abc",将其引用赋值给变量a。 1.25 new String(“abc”) 是去了哪里,仅仅是在堆里面吗?参考答案 在执行这句话时,JVM会先使用常量池来管理字符串直接量,即将"abc"存入常量池。然后再创建一个新的String对象,这个对象会被保存在堆内存中。并且,堆中对象的数据会指向常量池中的直接量。 1.26 接口和抽象类有什么区别?参考答案 从设计目的上来说,二者有如下的区别: 接口体现的是一种规范。对于接口的实现者而言,接口规定了实现者必须向外提供哪些服务;对于接口的调用者而言,接口规定了调用者可以调用哪些服务,以及如何调用这些服务。当在一个程序中使用接口时,接口是多个模块间的耦合标准;当在多个应用程序之间使用接口时,接口是多个程序之间的通信标准。 抽象类体现的是一种模板式设计。抽象类作为多个子类的抽象父类,可以被当成系统实现过程中的中间产品,这个中间产品已经实现了系统的部分功能,但这个产品依然不能当成最终产品,必须有更进一步的完善,这种完善可能有几种不同方式。 从使用方式上来说,二者有如下的区别: 接口里只能包含抽象方法、静态方法、默认方法和私有方法,不能为普通方法提供方法实现;抽象类则完全可以包含普通方法。 接口里只能定义静态常量,不能定义普通成员变量;抽象类里则既可以定义普通成员变量,也可以定义静态常量。 接口里不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。 接口里不能包含初始化块;但抽象类则完全可以包含初始化块。 一个类最多只能有一个直接父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多个接口可以弥补Java单继承的不足。 扩展阅读 接口和抽象类很像,它们都具有如下共同的特征: 接口和抽象类都不能被实例化,它们都位于继承树的顶端,用于被其他类实现和继承。 接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。 1.27 接口中可以有构造函数吗?参考答案 由于接口定义的是一种规范,因此接口里不能包含构造器和初始化块定义。接口里可以包含成员变量(只能是静态常量)、方法(只能是抽象实例方法、类方法、默认方法或私有方法)、内部类(包括内部接口、枚举)定义。 1.28 谈谈你对面向接口编程的理解参考答案 接口体现的是一种规范和实现分离的设计哲学,充分利用接口可以极好地降低程序各模块之间的耦合,从而提高系统的可扩展性和可维护性。基于这种原则,很多软件架构设计理论都倡导“面向接口”编程,而不是面向实现类编程,希望通过面向接口编程来降低程序的耦合。 总结说实话我都不相信有人能看完,那就让我赢吧 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 4:53:39- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |