Java基础知识面试题

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

Java概述

何为编程

编程就是使用某种程序设计语言编写程序代码让计算机解决某个问题

为了使计算机能够理解人的意图人类就必须要将需解决的问题的思路、方法、和手段通过计算机能够理解的形式告诉计算机使得计算机能够根据人的指令一步一步去工作完成某种特定的任务。这种人和计算机之间交流的过程就是编程。

什么是Java

Java是一门面向对象编程语言不仅吸收了C++语言的各种优点还摒弃了C++里难以理解的多继承、指针等概念因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表极好地实现了面向对象理论允许程序员以优雅的思维方式进行复杂的编程 。

jdk1.5之后的三大版本

1、Java SEJ2SEJava 2 Platform Standard Edition标准版

Java SE 以前称为 J2SE。它允许开发和部署在桌面、服务器、嵌入式环境和实时环境中使用的 Java 应用程序。Java SE 包含了支持 Java Web 服务开发的类并为Java EE和Java ME提供基础。

2、Java EEJ2EEJava 2 Platform Enterprise Edition企业版

Java EE 以前称为 J2EE。企业版本帮助开发和部署可移植、健壮、可伸缩且安全的服务器端Java 应用程序。Java EE 是在 Java SE 的基础上构建的它提供 Web 服务、组件模型、管理和通信 API可以用来实现企业级的面向服务体系结构service-oriented architectureSOA和 Web2.0应用程序。2018年2月Eclipse 宣布正式将 JavaEE 更名为 JakartaEE

3、Java MEJ2MEJava 2 Platform Micro Edition微型版

Java ME 以前称为 J2ME。Java ME 为在移动设备和嵌入式设备比如手机、PDA、电视机顶盒和打印机上运行的应用程序提供一个健壮且灵活的环境。Java ME 包括灵活的用户界面、健壮的安全模型、许多内置的网络协议以及对可以动态下载的连网和离线应用程序的丰富支持。基于 Java ME 规范的应用程序只需编写一次就可以用于许多设备而且可以利用每个设备的本机功能。

JVM、JRE和JDK的关系

1、JVM

Java Virtual Machine是Java虚拟机Java程序需要运行在虚拟机上不同的平台有自己的虚拟机因此Java语言可以实现跨平台。

2、JRE

Java Runtime Environment包括Java虚拟机和Java程序所需的核心类库等。核心类库主要是java.lang包包含了运行Java程序必不可少的系统类如基本数据类型、基本数学函数、字符串处理、线程、异常处理类等

3、JDK

Java Development Kit是提供给Java开发人员使用的其中包含了Java的开发工具也包括了JRE。所以安装了JDK 就无需再单独安装JRE了。其中的开发工具编译工具(javac.exe)打包工具(jar.exe)等

什么是跨平台性原理是什么

跨平台性是指软件能够在不同的操作系统和硬件平台上运行的能力。

Java具有跨平台性是因为Java字节码可以在不同的操作系统和硬件平台上运行而不需要对字节码进行修改这是Java虚拟机JVM的工作。

Java的跨平台原理是基于Java虚拟机JVM的。Java编译器将Java源代码编译成Java字节码然后Java虚拟机JVM将这些字节码加载到内存中并在运行时解释执行这些字节码。由于Java虚拟机JVM是与平台无关的因此Java程序可以在不同的操作系统和硬件平台上运行。

Java语言有哪些特点

  • 简单易学Java语言的语法与C语言和C++语言很接近
  • 面向对象封装继承多态
  • 平台无关性Java虚拟机实现平台无关性
  • 支持网络编程并且很方便Java语言诞生本身就是为简化网络编程设计的
  • 支持多线程多线程机制使应用程序在同一时间并行执行多项任务
  • 健壮性Java语言的强类型机制、异常处理、垃圾的自动收集等

什么是字节码采用字节码的最大好处是什么

1、字节码

Java源代码经过编译器例如IDEA软件编译后产生的文件即扩展为.class的文件它不面向任何特定的处理器只面向虚拟机。

2、采用字节码的好处

采用字节码的好处主要有以下几点

1. 跨平台性Java字节码是一种中间语言可以在不同的操作系统和硬件平台上运行这使得Java具有跨平台性。

2. 安全性Java字节码可以在运行时进行安全检查防止程序对系统造成损害。

3. 高效性Java字节码可以通过即时编译器JIT在运行时进行优化提高程序的执行效率。

4. 反编译困难Java字节码是一种二进制文件相对于源代码来说更难以被反编译这有助于保护程序的知识产权。

5. 动态性Java字节码可以在运行时动态生成和加载这使得Java具有更强的动态性和灵活性。

总之采用字节码是Java语言的一大特色也是Java成为一种广泛应用的编程语言的重要原因之一。

3、编译与解释并存

Java中引入了虚拟机的概念即在机器和编译程序之间加入了一层抽象的虚拟机器。这台虚拟的机器在任何平台上都提供给编译程序一个的共同的接口。编译程序只需要面向虚拟机生成虚拟机能够理解的代码然后由解释器来将虚拟机代码转换为特定系统的机器码执行。在Java中这种供虚拟机理解的代码叫做字节码即扩展为.class的文件它不面向任何特定的处理器只面向虚拟机。Java源程序经过虚拟机编译器编译后变成字节码字节码由虚拟机解释器执行解释器将其翻译成特定机器上的机器码然后在特定的机器上运行这就是上面提到的Java的特点的编译与解释并存的解释。

流程Java源代码---->编译器---->jvm可执行的Java字节码(即虚拟指令)---->jvm把字节码发给解释器----->解释器将其翻译成特定机器上的机器码------>程序运行。

补充不同操作系统的jvm虚拟机是一样的吗

不同操作系统的JVM虚拟机是不一样的。尽管Java虚拟机规范定义了Java虚拟机的行为和接口但是Java虚拟机的实现是与操作系统相关的因为Java虚拟机必须与操作系统交互来实现Java程序的执行。

不同操作系统的JVM虚拟机在实现上可能存在一些差异比如内存管理、线程调度、垃圾回收等方面的实现会因为操作系统的不同而有所不同。此外JVM虚拟机还需要与操作系统交互来实现一些底层功能比如文件操作、网络通信等这些功能的实现也会因为操作系统的不同而有所不同。

尽管不同操作系统的JVM虚拟机存在一些差异但是它们都遵循Java虚拟机规范保证了Java程序在不同操作系统上的可移植性和跨平台性。

什么是Java程序的主类

Java 应用程序是由若干类和接口组成的为了使Java 应用程序能够运行至少要有一个类含有main()主方法因为main()主方法是Java 应用程序的入口点Java 应用程序就是从这里开始运行的我们把含有main()主方法的类称为Java 应用程序的主类。

骚戴理解很容易误认为有public的类就是主类注意有public的类不一定是主类。在Java程序中可以有多个public类但是只能有一个主类Main Class主类是程序的入口点是Java虚拟机启动程序时调用的类。

在Java程序中主类必须包含一个public static void main(String[] args)方法这个方法是程序的入口点Java虚拟机会从这个方法开始执行程序。因此只有包含这个方法的类才能作为主类。

如果有多个public类都包含了main方法那么只有其中一个类可以作为主类其他类只能作为辅助类。如果没有public类包含main方法那么程序将无法运行。

Java 应用程序是从main()主方法开始运行的在Java 应用程序中将含有main()主方法的类称为主类而在一个Java 应用程序中开发人员往往为了测试的方便会为每个类添加一个主方法但是在测试完以后应该记着将进行测试的主方法删除只保留主类中唯一的一个主方法。

Java和C++的区别

Java和C++是两种不同的编程语言它们之间存在以下几点区别

1. 语言类型Java是一种面向对象的编程语言而C++既可以面向过程编程也可以面向对象编程。

2. 平台依赖性Java具有很好的跨平台性可以在不同的操作系统和硬件平台上运行而C++的程序在不同的操作系统上需要重新编译。

3. 内存管理Java具有自动内存管理机制程序员不需要手动管理内存而C++需要程序员手动进行内存管理。

4. 异常处理Java中的异常处理机制比C++更加完善和规范。

5. 安全性Java具有更好的安全性可以防止程序对系统造成损害而C++程序可以访问系统的底层资源存在一定的安全风险。

6. 开发效率Java的开发效率比C++更高Java具有更丰富的类库和工具可以快速开发出高质量的应用程序。

总之Java和C++是两种不同的编程语言它们各有优缺点适用于不同的场景和需求。Java适合于开发跨平台的应用程序和Web应用而C++适合于开发需要高性能和底层控制的应用程序如操作系统、驱动程序等。

基础语法

Java有哪些数据类型

定义Java语言是强类型语言对于每一种数据都定义了明确的具体的数据类型在内存中分配了不同大小的内存空间。

Java的引用数据类型包括以下几种

1. 类类型Class Type类类型是指由类定义的数据类型包括自定义类和Java API中的类。

2. 接口类型Interface Type接口类型是指由接口定义的数据类型接口定义了一组方法的签名而不包含方法的实现。

3. 数组类型Array Type数组类型是指由相同数据类型的元素组成的有序集合可以通过下标来访问每个元素。

4. 枚举类型Enumeration Type枚举类型是指由一组具名常量组成的数据类型每个常量都有一个唯一的名称和一个预定义的值。

5. 注解类型Annotation Type注解类型是指由注解定义的数据类型注解用于为程序元素添加元数据。

除了以上几种引用数据类型Java还有一种特殊的引用类型——null类型它表示一个空引用。当一个引用变量没有指向任何对象时它的值为null。

8种java基本数据类型的详细解释

8种java基本数据类型的详细解释

在JDK8的官方文档中boolean类型仍然占用1个字节8位。Java中的boolean类型只有两个取值true和false因此只需要1个字节的存储空间就可以表示它们。在JDK8中boolean类型的范围仍然是true和false没有发生变化因此它的存储空间也没有发生变化。

骚戴理解char占两个字节在Java中String类型是一个引用类型它表示一个字符串对象。String对象占用的字节数取决于字符串的长度和编码方式。

在Java中字符串默认使用UTF-16编码每个字符占用2个字节。因此如果一个字符串包含n个字符则它占用的字节数为2n个字节。例如字符串"Hello World"包含11个字符因此它占用的字节数为22个字节。

需要注意的是字符串对象本身也需要一定的存储空间包括对象头和其他元数据。在32位Java虚拟机中一个空的String对象占用16个字节在64位Java虚拟机中一个空的String对象占用24个字节。因此实际上一个字符串对象占用的总字节数为2n加上对象头和其他元数据的字节数。

switch 是否能作用在 byte 上是否能作用在 long 上是否能作用在 String 上

在Java中switch语句可以作用在byte、short、char、int、枚举类型和String类型上但不能作用在long类型上。

具体来说

1. byte、short、char、int和枚举类型这些类型可以作为switch语句的表达式类型因为它们的取值范围是有限的可以作为case语句的常量表达式。

2. long类型由于long类型的取值范围较大无法枚举所有的取值因此不能作为switch语句的表达式类型。

3. String类型从Java SE 7开始String类型也可以作为switch语句的表达式类型。在使用String类型作为表达式时case语句后面跟的是字符串常量而不是整数常量。

需要注意的是无论是哪种类型case语句后面的值必须是常量表达式不能是变量或者其他表达式。

用最有效率的方法计算 2 乘以 8

计算2乘以8最有效率的方法是使用位运算。因为位运算比乘法运算和除法运算更快。

具体来说可以使用左移运算符<<实现2乘以8的计算。左移运算符将一个二进制数向左移动指定的位数相当于将这个数乘以2的指定次幂。

对于2乘以8可以将2左移3位即2 << 3得到的结果为16。因此最有效率的方法计算2乘以8的结果为16。

Math.round(11.5) 等于多少Math.round(-11.5)等于多少

Math.round(11.5)的返回值是 12Math.round(-11.5)的返回值是-11。Math.round是取大于这个数的最小整数

骚戴理解通俗易懂的讲就是四舍五入

float f=3.4;是否正确

不正确3.4 是双精度数将双精度型double赋值给浮点型float属于下转型down-casting也称为窄化 会造成精度损失因此需要强制类型转换float f =(float)3.4; 或者写成 float f =3.4F; 

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语言采用Unicode字符集来表示字符和字符串Unicode字符集是一种全球通用的字符编码方案它包含了世界上几乎所有的字符包括各种语言的字母、数字、符号、汉字等。Java中的char类型和String类型都使用Unicode字符集来表示。

在Java中Unicode字符集采用UTF-16编码方式进行存储每个字符占用2个字节。UTF-16编码方式是一种可变长的编码方式在表示ASCII字符时只需要一个字节而在表示非ASCII字符时需要两个字节或者四个字节。UTF-16编码方式可以保证所有的Unicode字符都能被正确地表示并且支持快速的随机访问和长度计算。

Java中的字符串常量也采用Unicode字符集和UTF-16编码方式进行存储。在Java源代码中可以直接使用Unicode转义序列来表示任意的Unicode字符例如"\u4e2d\u6587"表示中文。Java编译器会将这些转义序列转换为对应的Unicode字符然后存储到class文件中。在程序运行时Java虚拟机会将这些Unicode字符转换为UTF-16编码方式并存储到内存中。

Java注释

1、Java注释的定义

定义:用于解释说明程序的文字分类

2、Java注释的分类

单行注释格式 // 注释文字

多行注释格式 /* 注释文字 */

文档注释格式/** 注释文字 */

3、Java注释的作用

在程序中尤其是复杂的程序中适当地加入注释可以增加程序的可读性有利于程序的修改、调试和交流。注释的内容在程序编译的时候会被忽视不会产生目标代码注释的部分不会对程序的执行结果产生任何影响。多行和文档注释都不能嵌套使用。

访问修饰符

1、访问修饰符定义

Java中可以使用访问修饰符来保护对类、变量、方法和构造方法的访问。Java 支持 4 种不同的访问权限。

2、访问修饰符分类

private : 在同一类内可见。使用对象变量、方法。 注意不能修饰类外部类

default (即缺省什么也不写不使用任何关键字: 在同一包内可见不使用任何修饰符。使用对象类、接口、变量、方法。

protected : 对同一包内的类和不同包的所有子类可见。使用对象变量、方法。 注意不能修饰类外部类。

public : 对所有类可见。使用对象类、接口、变量、方法

3、访问修饰符图

&和&&的区别

&和&&的区别

&和&&都是Java中的逻辑运算符用于判断两个布尔表达式的逻辑关系。它们的区别如下

1. &是逻辑与运算符它的两个操作数都会被求值只有当两个操作数都为true时结果才为true。即使第一个操作数为false第二个操作数也会被求值。

2. &&也是逻辑与运算符它的两个操作数都会被求值但是当第一个操作数为false时第二个操作数不会被求值直接返回false。只有当第一个操作数为true时才会继续求值第二个操作数。

因此&&运算符具有短路求值的特点可以避免不必要的计算提高程序的效率。在实际开发中如果两个操作数都是常量或者字面量建议使用&&运算符可以避免不必要的计算。

需要注意的是&和&&的优先级不同&&的优先级比&高。在使用时应该根据实际需要加上括号以避免优先级问题导致的错误。

Java 有没有 goto

Java中没有goto语句这是由Java语言设计者故意决定的。goto语句是一种无限制的跳转语句它可以跳转到程序中的任何一行代码包括循环、条件语句等。goto语句虽然可以简化程序的编写但是会使程序结构变得混乱和难以理解容易引起逻辑错误和调试困难。

为了避免goto语句的缺陷Java语言设计者采用了其他的控制结构来代替goto语句比如循环语句、条件语句、异常处理语句等。这些控制结构可以更加清晰地表达程序的逻辑结构使程序更容易理解和维护。

需要注意的是Java中有一个标签Label语句可以用于给代码块起一个名字但是它不能像goto语句一样随意跳转到任何一行代码只能用于在嵌套的循环语句或者switch语句中跳出多层循环或者switch语句。在实际开发中应该避免使用标签语句以保持程序的简洁和清晰。

在Java中没有goto语句也不推荐使用。但是可以使用标签Label语句来跳出多层循环。下面是一个使用标签语句来跳出多层循环的示例代码

outer:
for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 10; j++) {
        if (i * j > 50) {
            System.out.println("i=" + i + ", j=" + j);
            break outer;
        }
    }
}

在这个示例代码中使用了一个outer标签来标识外层循环。当内层循环中的条件满足时使用break outer语句来跳出外层循环从而实现了跳出多层循环的效果。

需要注意的是使用标签语句来跳出多层循环虽然可以实现跳转的效果但是会使程序结构变得复杂和难以理解容易引起逻辑错误和调试困难。在实际开发中应该尽量避免使用标签语句以保持程序的简洁和清晰。

final 有什么用

用于修饰类、属性和方法

  1. 被final修饰的类不可以被继承
  2. 被final修饰的方法不可以被重写
  3. 被final修饰的变量不可以被改变

final finally finalize区别

final、finally和finalize是Java中三个不同的关键字它们的作用和用法各不相同。它们的区别如下

1. final关键字用于修饰类、方法和变量表示它们是不可变的。final修饰的类不能被继承final修饰的方法不能被重写final修饰的变量不能被重新赋值。final关键字可以提高程序的性能和安全性避免意外的修改。

2. finally关键字用于定义一个代码块无论try语句块中是否发生异常finally语句块中的代码总是会被执行。finally语句块通常用于释放资源、关闭文件、清理数据等操作保证程序的健壮性和可靠性。

3. finalize方法是Object类中的一个方法用于在垃圾回收器回收对象之前执行一些清理操作。finalize方法在对象被垃圾回收之前被调用可以用于释放资源、关闭文件等操作但是不建议这么做。但是finalize方法不是Java中的析构函数不能保证一定被调用也不能保证在对象被回收之前立即执行。

finalize方法是Java中的垃圾回收机制的一部分它在垃圾回收器回收对象之前被调用但是不能保证在对象被回收之前立即执行。这是因为Java的垃圾回收机制是异步的垃圾回收器并不会立即回收对象而是在一定的时间间隔后才会启动垃圾回收器进行回收。因此finalize方法的执行时间是不确定的可能会被延迟到下一次垃圾回收时才被执行。

另外由于finalize方法的执行时间不确定因此不能在finalize方法中进行重要的操作比如释放系统资源、关闭文件等操作。如果在finalize方法中进行这些操作可能会导致系统资源泄漏、文件无法关闭等问题。

因此建议在Java程序中尽量避免使用finalize方法而是采用其他方式来释放系统资源和清理数据比如使用try-with-resources语句块来自动关闭文件等资源。

需要注意的是final、finally和finalize虽然名称相似但是它们的作用和用法是完全不同的不要混淆使用。

this关键字的用法

this是自身的一个对象代表当前对象本身

this的用法在java中大体可以分为3种

1、普通的直接引用this相当于是指向当前对象本身。

2、在构造方法里形参与成员名字重名用this来区分

publicPerson(String name,int age){

this.name = name;

this.age = age;

}

3、引用本类的构造函数

classPerson{

private String name;

private int age;

public Person(){}

public Person(String name){

this.name = name;

}

public Person(String name,int age){

this(name);

this.age = age;

}

}

this参数调用本类中另一种形式的构造函数应该为构造函数中的第一条语句。

super关键字的用法

super相当于是指向当前对象的父类的引用

super也有三种用法

1.普通的直接引用

与this类似super相当于是指向当前对象父类的引用这样就可以用super.xxx来引用父类的成员。

2.子类中的成员变量或方法与父类中的成员变量或方法同名时用super进行区分

class Person{

    protected String name;

    public Person(String name){

        this.name = name;

    }

}

class Student extends Person{

    private String name;

    public Student(String name, String name1){

        super(name);

    this.name = name1;

}

publicvoidgetInfo(){

    System.out.println(this.name);//Child
    
    System.out.println(super.name);//Father

    }

}

publicclassTest{

public static void main(String[] args){
    
    Student s1=newStudent("Father","Child");

    s1.getInfo();

    }

}

3、引用父类构造函数

super参数调用父类中的某一个构造函数应该为构造函数中的第一条语句。

this与super的异同

super: 它引用当前对象的直接父类中的成员用来访问直接父类中被隐藏的父类中成员数据或函数基类与派生类中有相同成员定义时如super.变量名 super.成员函数据名实参

this它代表当前对象名在程序中易产生二义性之处应使用this来指明当前对象如果函数的形参与类中的成员数据同名这时需用this来指明成员变量名

1、super()和this()的区别

  • this关键字用于引用当前对象可以用于访问当前对象的成员变量和成员方法。this关键字还可以用于调用当前类的其他构造方法以实现构造方法的重载。

  • super关键字用于引用父类对象可以用于访问父类的成员变量和成员方法。super关键字还可以用于调用父类的构造方法以实现子类的构造方法。

  • this关键字可以省略Java编译器会自动判断当前对象是否需要使用this关键字。而super关键字不能省略必须显式地指定父类的构造方法。

2、super()和this()的相同点

  • super()和this()均需放在构造方法内第一行。
  • this和super都必须在构造方法或者实例方法中使用不能在static环境中使用。包括static变量,static方法static语句块。
  • this和super都可以用于访问对象的成员变量、成员方法和构造方法

3、注意事项

this和super不能同时出现在一个构造函数里面是因为它们都是用于调用构造方法的关键字使用它们会引起歧义。在Java中构造函数的第一行必须是调用本类或父类的构造方法如果同时使用this和super编译器无法确定应该调用哪一个构造方法会导致编译错误。

static存在的主要意义

static是Java中的一个关键字它可以用于修饰变量、方法和代码块。static存在的主要意义如下

1. 共享内存空间。使用static修饰的变量和方法都属于类的静态成员它们只会在内存中存在一份被所有的对象共享。这样可以节省内存空间避免重复创建对象和数据冗余。

2. 方便访问。使用static修饰的变量和方法可以通过类名直接访问不需要先创建对象。这样可以方便地访问类的静态成员避免了创建对象的麻烦。

3. 实现工具类和单例模式。使用static修饰的方法可以实现工具类和单例模式。工具类中的方法通常不需要创建对象可以直接调用而单例模式中的对象只需要创建一次可以使用静态变量和静态方法来实现。

4. 初始化顺序。使用static修饰的代码块和变量会在类加载时被初始化可以用于控制初始化顺序和实现一些复杂的初始化操作。

需要注意的是static虽然具有很多优点但是也有一些缺点。使用static会增加代码的耦合性和复杂性可能会导致线程安全问题和内存泄漏问题。因此在使用static时需要谨慎考虑避免滥用。

1、为什么说static块可以用来优化程序性能

static块可以用来优化程序性能的原因是因为它可以在类被加载时执行而不需要等到类被实例化时才执行。这样可以避免在类被实例化时重复执行一些初始化操作从而提高程序的性能。因此很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。

补充static块可以置于类中的任何地方类中可以有多个static块。在类初次被加载的时候会按照static块的顺序来执行每个static块并且只会执行一次。

2、static的独特之处

1、被static修饰的变量或者方法是独立于该类的任何对象也就是说这些变量和方法不属于任何一个实例对象而是被类的实例对象所共享。

2、在该类被第一次加载的时候就会去加载被static修饰的部分而且只在类第一次使用时加载并进行初始化注意这是第一次用就要初始化后面根据需要是可以再次赋值的。

3、static变量值在类加载的时候分配空间以后创建类对象的时候不会重新分配。赋值的话是可以任意赋值的

4、被static修饰的变量或者方法是优先于对象存在的也就是说当一个类加载完毕之后即便没有创建对象也可以去访问。

3、static应用场景

1. 静态成员。使用static修饰的变量、方法和代码块都属于类的静态成员它们只会在内存中存在一份被所有的对象共享。这样可以节省内存空间避免重复创建对象和数据冗余。

2. 静态方法。使用static修饰的方法可以直接通过类名调用不需要先创建对象。静态方法不依赖于任何对象可以被多个对象共享因此可以用于实现工具类和单例模式。

3. 静态代码块。使用static修饰的代码块会在类被加载时执行可以用于实现一些复杂的初始化操作避免在类被实例化时重复执行一些初始化操作。

4. 静态内部类。使用static修饰的内部类属于类的静态成员可以直接通过类名访问。静态内部类不依赖于外部类的实例可以独立存在因此可以用于实现一些工具类和辅助类。

4、static注意事项

1、静态只能访问静态。

2、非静态既可以访问非静态的也可以访问静态的。

break ,continue ,return 的区别及作用

return

return关键字并不是专门用于跳出循环的return的功能是结束一个方法。 一旦在循环体内执行到一个return语句return语句将会结束该方法循环自然也随之结束。与continue和break不同的是return直接结束整个方法不管这个return处于多少层循环之内。

public class ReturnTest
{
  public static void main(String[] args){
    // 一个简单的for循环
    for (int i = 0; i < 3 ; i++ ){
      System.out.println("i的值是" + i);
      if (i == 1){
        return;
      }
      System.out.println("return后的输出语句");
    }
  }
}

continue

continue的功能和break有点类似区别是continue只是中止本次循环接着开始下一次循环。而break则是完全中止循环。

public class ContinueTest
{
  public static void main(String[] args){
// 一个简单的for循环
    for (int i = 0; i < 3 ; i++ ){
        for (int j = 0; j < 3 ; j++ ){.
            System.out.println("j的值是" + j);
       if (j == 1){
         // 忽略本次循环的剩下语句就是除了j=1不打印以外其他的都打印
         continue;
       }
       System.out.println("continue后的输出语句");
    }
  }
}

break

break用于完全结束一个循环跳出循环体。不管是哪种循环一旦在循环体中遇到break系统将完全结束循环开始执行循环之后的代码。 break不仅可以结束其所在的循环还可结束其外层循环。此时需要在break后紧跟一个标签这个标签用于标识一个外层循环。Java中的标签就是一个紧跟着英文冒号:的标识符。且它必须放在循环语句之前才有作用。

public class BreakTest
{
  public static void main(String[] args){
    // 外层循环outer作为标识符
    outer:
    for (int i = 0 ; i < 5 ; i++ ){
      // 内层循环
      for (int j = 0; j < 3 ; j++ ){
        System.out.println("i的值为:" + i + " j的值为:" + j);
        if (j == 1){
          // 跳出outer标签所标识的循环。
           break outer;
        }
      }
    }
  }
}

骚戴理解注意break本身只能跳出一层循环像上面的的第二层循环能跳出但是不会跳出第一层循环但是结合标识符就可以跳出多层嵌套循环

在 Java 中如何跳出当前的多重嵌套循环

在Java中要跳出当前的多重嵌套循环可以使用标签label和break语句结合使用。例如

outer: 
for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 10; j++) {
        if (j == 5) {
            break outer;
        }
        System.out.println("i=" + i + ", j=" + j);
    }
}

骚戴理解在上面的代码中使用outer标签标记了外层循环在内层循环中当j等于5时使用break outer语句跳出多重嵌套循环。当程序执行到这个语句时会跳转到outer标签处从而跳出多重嵌套循环。 注意上面的代码在执行到 `break outer;` 时会立即跳出多重循环不会继续执行之后的代码。

面向对象

面向对象和面向过程

面向过程的优缺点

优点性能比面向对象高因为类调用时需要实例化开销比较大比较消耗资源;比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发性能是最重要的因素。

缺点没有面向对象易维护、易复用、易扩展

面向对象的优缺点

优点易维护、易复用、易扩展由于面向对象有封装、继承、多态性的特性可以设计出低耦合的系统使系统更加灵活、更加易于维护

缺点性能比面向过程低

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

面向对象和面向过程是两种不同的编程思想。面向过程是按照步骤一步一步地解决问题强调的是数据和行为的分离而面向对象则是将问题看作是由许多对象组成的强调的是数据和行为的统一。面向过程的程序设计更加注重程序的执行过程而面向对象的程序设计更加注重对象之间的交互。在面向对象的程序设计中我们将问题看作是由许多对象组成的每个对象都有自己的属性和方法对象之间通过方法来进行交互从而完成程序的功能。而在面向过程的程序设计中我们则是按照步骤一步一步地解决问题强调的是数据和行为的分离。

java中子类会继承父类的构造方法吗

Java中的子类会默认继承父类的构造方法但是子类的构造方法并不完全继承父类的构造方法。具体来说子类构造方法会自动调用父类的无参构造方法如果父类没有无参构造方法则需要在子类的构造方法中显式调用父类的有参构造方法。如果子类的构造方法没有显式调用父类的构造方法则编译器会自动在子类的构造方法中插入一条隐式的调用语句 `super();`该语句会调用父类的无参构造方法。如果父类没有无参构造然后子类又没有显性的引用父类的有参构造的话就会报错

如果父类中有多个构造方法子类可以选择调用其中一个构造方法也可以在自己的构造方法中添加额外的参数以便调用父类的有参构造方法。

需要注意的是如果父类中的构造方法是private类型的则子类无法继承该构造方法因为private类型的构造方法只能在本类中访问无法被子类继承和调用。

总之Java中的子类会默认继承父类的构造方法但需要注意构造方法的访问控制符以及在子类构造方法中是否显式调用了父类的构造方法。

java中子类会继承父类的private方法和属性吗

Java中的子类不会继承父类的私有private方法和属性。私有方法和属性只能在本类中访问无法被子类继承和访问。这是因为私有方法和属性的访问控制符是private只能在本类中被访问其他类无法访问。因此子类无法继承父类的私有方法和属性。

如果父类中有一些私有方法和属性需要在子类中使用可以将其改为protected类型这样子类就可以继承和访问了。protected类型的方法和属性可以被子类继承和访问同时也可以在同一包内的其他类中访问。

需要注意的是如果父类中的方法和属性是default类型即没有访问控制符则子类可以继承和访问但是只限于同一包内的子类。如果子类和父类不在同一包内则无法继承和访问父类的default方法和属性。

面向对象的特征有哪些方面

1、抽象

抽象是将一类对象的共同特征总结出来构造类的过程包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为并不关注这些行为的细节是什么。

其中Java 面向对象编程三大特性封装 继承 多态

2、封装

Java的特性封装是通过使用访问控制符我们可以将类的内部实现细节隐藏起来只暴露出必要的接口从而保证了数据的安全性和完整性。

在Java中封装是通过访问控制符来实现的。Java提供了四种访问控制符public、protected、private和default。其中public表示公共的可以在任何地方访问protected表示受保护的只能在当前类、同一包内的类和子类中访问private表示私有的只能在当前类中访问default表示默认的只能在同一包内的类中访问。

3、继承

继承是使用已存在的类的定义作为基础建立新类的技术新类的定义可以增加新的数据或新的功能也可以用父类的功能但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。

关于继承如下 3 点请记住

1、子类拥有父类非 private 的属性和方法。

2、子类可以拥有自己属性和方法即子类可以对父类进行扩展。

3、子类可以用自己的方式实现父类的方法重写。

4、多态

Java的特性多态是指同一种行为或方法可以在不同的对象上具有不同的实现方式和表现形式。具体来说多态性是指一个对象可以被看作是多种类型的实例这些类型可以是它本身的类型也可以是它的父类或接口的类型。

Java中的多态性主要通过两种方式实现继承和接口。

  • 通过继承子类可以继承父类的方法和属性同时也可以重写父类的方法从而实现不同的行为。
  • 通过接口类可以实现多个接口从而具有多种行为和表现形式。

在Java中多态性的实现主要依赖于动态绑定机制。在程序运行时系统会根据对象的实际类型来调用相应的方法而不是根据变量的类型来调用方法。这样就可以实现同一种方法在不同对象上的不同实现方式和表现形式从而实现多态性。

多态性的好处是可以提高程序的灵活性和可扩展性同时也可以提高代码的可读性和可维护性。因此在Java程序设计中多态性是一个非常重要的特性。

什么是多态机制Java语言是如何实现多态的

什么是多态机制

多态机制是一种面向对象编程的机制多态是指同一种行为或方法可以在不同的对象上具有不同的实现方式和表现形式。具体来说多态性是指一个对象可以被看作是多种类型的实例这些类型可以是它本身的类型也可以是它的父类或接口的类型。

Java语言是如何实现多态的

Java语言是一种支持多态机制的编程语言它通过继承和接口实现多态性。

Java中的多态性是通过动态绑定Dynamic Binding实现的。动态绑定是指在运行时根据对象的实际类型来确定调用哪个方法或属性。当一个方法被调用时编译器并不知道实际调用的是哪个方法而是在运行时根据对象的实际类型来确定调用哪个方法。这样就可以实现同一种方法在不同对象上的不同实现方式和表现形式从而体现多态性。

具体来说Java中的多态性主要通过两种方式实现继承和接口。通过继承子类可以继承父类的方法和属性同时也可以重写父类的方法从而实现不同的行为。通过接口类可以实现多个接口从而具有多种行为和表现形式。

在Java中多态性的实现主要依赖于动态绑定机制。在程序运行时系统会根据对象的实际类型来调用相应的方法而不是根据变量的类型来调用方法。这样就可以实现同一种方法在不同对象上的不同实现方式和表现形式从而实现多态性。

总之Java中的多态机制是一种非常重要的面向对象编程的机制它可以提高程序的灵活性和可扩展性同时也可以提高代码的可读性和可维护性。

面向对象五大基本原则是什么

1、单一职责原则SRP(Single Responsibility Principle)

类的功能要单一不能包罗万象跟杂货铺似的。

2、开放封闭原则OCP(OpenClose Principle)

一个模块对于拓展是开放的对于修改是封闭的

3、里式替换原则LSP(the Liskov Substitution Principle LSP)

子类可以替换父类出现在父类能够出现的任何地方。

4、依赖倒置原则DIP(the Dependency Inversion Principle DIP)

依赖倒置原则Dependency Inversion Principle简称DIP是面向对象设计中的一条重要原则它是SOLID原则中的一部分。它的核心思想是高层模块不应该依赖于低层模块二者都应该依赖于抽象抽象不应该依赖于细节而细节应该依赖于抽象。

这个原则的意思是我们在设计类和模块时应该尽量使用抽象类或接口来定义类之间的依赖关系而不是使用具体的实现类。这样做的好处是可以降低模块之间的耦合度提高代码的可维护性和可扩展性。同时也可以提高代码的重用性和测试性。

具体来说依赖倒置原则可以通过以下几个方面来实现

1. 高层模块不应该依赖于低层模块这意味着我们应该尽量避免在高层模块中直接使用低层模块的具体实现类而应该使用抽象类或接口来定义依赖关系。

2. 二者都应该依赖于抽象这意味着我们应该尽量使用抽象类或接口来定义类之间的依赖关系而不是使用具体的实现类。

3. 抽象不应该依赖于细节这意味着我们应该尽量避免在抽象类或接口中使用具体的实现类而应该将细节留给具体的实现类去处理。

4. 细节应该依赖于抽象这意味着我们应该尽量使用抽象类或接口来定义类之间的依赖关系而具体的实现类应该依赖于抽象类或接口。

总之依赖倒置原则是一条非常重要的面向对象设计原则它可以帮助我们设计出更加灵活、可维护、可扩展和可测试的代码。

5、接口分离原则ISP(the Interface Segregation Principle ISP)

接口分离原则Interface Segregation Principle简称ISP是面向对象设计中的一条重要原则它是SOLID原则中的一部分。它的核心思想是一个类不应该依赖于它不需要的接口一个类应该尽量少地依赖于其他类。

这个原则的意思是我们在设计接口时应该尽量将接口拆分成多个小的、专门的接口而不是设计一个大而全的接口。这样做的好处是可以降低类之间的耦合度提高代码的可维护性和可扩展性。同时也可以提高代码的重用性和灵活性。

具体来说接口分离原则可以通过以下几个方面来实现

1. 将接口拆分成多个小的、专门的接口这意味着我们应该尽量将接口中的方法拆分成多个小的、专门的接口而不是设计一个大而全的接口。

2. 避免接口过于臃肿这意味着我们应该尽量避免在接口中定义过多的方法而应该将方法拆分成多个小的、专门的接口。

3. 接口与实现相分离这意味着我们应该尽量将接口与实现相分离将接口定义在独立的模块中而具体的实现类则实现接口。

4. 不强制实现不需要的接口这意味着我们应该尽量避免在一个类中实现不需要的接口而应该只实现该类需要的接口。

总之接口分离原则是一条非常重要的面向对象设计原则它可以帮助我们设计出更加灵活、可维护、可扩展和可测试的代码。

抽象类和接口的对比

抽象类和接口都是Java中实现抽象的机制但是它们有一些异同之处。

相同点

1. 都不能被实例化只能被继承或实现。

2. 都可以包含抽象方法需要子类或实现类去实现。

3. 都可以包含具体的方法实现。

4. 都可以用来实现多态性。

不同点

1. 抽象类可以包含抽象方法和具体方法而接口只能包含抽象方法。

2. 一个类只能继承一个抽象类但是可以实现多个接口。

3. 抽象类可以有构造方法而接口没有构造方法。

4. 抽象类的子类可以覆盖父类的方法也可以不覆盖而实现接口的类必须实现接口中的所有方法。

5. 抽象类可以有成员变量而接口只能有常量。

6. 抽象类的访问修饰符可以是public、protected或默认的而接口的访问修饰符只能是public。

7. 抽象类用于表示一种“is-a”的关系即子类是父类的一种特殊类型而接口用于表示一种“has-a”的关系即类具有某种能力或特征。

总之抽象类和接口都是Java中实现抽象的机制它们都有自己的优缺点和适用场景我们需要根据具体的需求来选择使用哪种方式。

java中接口的方法能不能有方法体

在Java 8之前接口中的方法都是抽象方法不能有方法体。但是从Java 8开始接口中可以定义默认方法default method和静态方法static method这两种方法都可以有方法体。

默认方法是指在接口中定义的有方法体的方法可以被实现该接口的类直接继承或重写。默认方法的关键字是default例如


public interface MyInterface {
    default void myMethod() {
        System.out.println("This is a default method.");
    }
}

静态方法是指在接口中定义的有方法体的静态方法可以直接通过接口名调用。静态方法的关键字是static例如


public interface MyInterface {
    static void myStaticMethod() {
        System.out.println("This is a static method.");
    }
}

需要注意的是接口中的默认方法和静态方法都有方法体但是抽象方法没有方法体。默认方法和静态方法的出现使得接口的功能更加强大可以更好地支持代码的复用和扩展。

普通类和抽象类有哪些区别

普通类和抽象类的主要区别在于

1. 实例化普通类可以被直接实例化而抽象类不能被直接实例化只能被继承。

2. 抽象方法普通类中不能包含抽象方法而抽象类中可以包含抽象方法。

3. 方法实现普通类中的方法必须有具体的实现而抽象类中的抽象方法没有具体的实现需要子类去实现。

4. 继承普通类可以被其他类继承而抽象类只能被继承不能被实例化。

5. 关系普通类表示一种具体的对象而抽象类表示一种抽象的概念或模板。

6. 使用普通类通常用于封装数据和行为而抽象类通常用于定义一些通用的行为和属性以便子类继承和实现。

总之普通类和抽象类都是Java中的类它们有自己的特点和适用场景。我们需要根据具体的需求来选择使用哪种类型的类。

骚戴理解需要注意的是抽象类里面可以有抽象方法和非抽象方法也就是有方法体的方法所以抽象类里的方法不一定都是抽象方法但是如果一个类里有抽象方法那么这个类一定是抽象类

抽象类能使用 final 修饰吗

不能定义抽象类就是让其他类继承的如果定义为 final 该类就不能被继承这样彼此就会产生矛盾所以 final 不能修饰抽象类

创建一个对象用什么关键字对象实例与对象引用有何不同

new关键字

对象实例与对象引用有何不同

  • new创建对象实例对象实例在堆内存中对象引用指向对象实例对象引用存放在栈内存中。
  • 在Java中一个对象引用可以指向0个或1个对象【一个对象引用可以为null表示它没有指向任何对象】。同时一个对象可以有n个引用指向它也就是说多个对象引用可以指向同一个对象实例。这种情况下我们可以通过任意一个对象引用来访问该对象实例的数据和方法。这种多个引用指向同一个对象实例的情况在Java中被称为对象的共享Object Sharing。对象的共享可以节省内存空间提高程序的效率

解释java中的变量、成员变量、局部变量的概念

在Java中变量是指在内存中分配的一块空间用于存储数据。Java中的变量分为以下两种类型

1. 局部变量定义在方法、代码块或者方法的参数列表中只在定义它的方法、代码块或者参数列表中有效出了这个范围就无法访问。局部变量必须显式地初始化否则编译器会报错。

2. 成员变量定义在类中方法外可以被类中的所有方法访问。成员变量有默认值如果没有显式地初始化会根据类型自动赋予默认值。成员变量可以有不同的访问权限例如public、private、protected和默认的访问权限。

成员变量与局部变量的区别有哪些

成员变量和局部变量是Java中的两种变量它们有以下区别

  1. 定义位置成员变量定义在类中方法外局部变量定义在方法中、代码块中或者方法的参数列表中。

  2. 作用域成员变量的作用域是整个类可以被类中的所有方法访问局部变量的作用域仅限于定义它的方法、代码块或者参数列表。

  3. 生命周期成员变量的生命周期与对象的生命周期一样当对象被创建时成员变量就被创建当对象被销毁时成员变量也被销毁。局部变量的生命周期在方法、代码块或者参数列表执行完毕后就结束了。

  4. 默认值成员变量有默认值例如int类型的成员变量默认值为0局部变量没有默认值必须在使用之前进行初始化。

  5. 访问权限成员变量可以有不同的访问权限例如public、private、protected和默认的访问权限局部变量没有访问权限的概念。

  6. 内存分配成员变量在对象创建时分配内存空间局部变量在定义时分配内存空间当作用域结束时内存空间被释放。

使用原则

在Java中使用变量时需要遵循就近原则Nearby Principle即优先使用离当前位置最近的变量。就近原则的具体实现方式如下

1. 首先在当前作用域方法、代码块、参数列表中查找变量如果找到了同名的变量就使用它。

2. 如果在当前作用域中没有找到同名的变量就在外层作用域如果有的话中查找同名的变量直到找到为止。

3. 如果在所有作用域中都没有找到同名的变量就会报编译错误。

需要注意的是如果局部变量和成员变量同名就近原则会优先使用局部变量如果要使用成员变量需要使用this关键字来指定。

在Java中定义没有参数的构造方法无参构造的作用

在Java中定义没有参数的构造方法的作用主要有以下两个方面

1. 初始化对象的成员变量Java中的无参构造方法可以在对象创建时对成员变量进行初始化确保对象在使用之前具有合理的状态。如果一个类定义了有参构造方法那么子类必须显式地调用父类的有参构造方法以确保父类的成员变量得到了初始化。

2. 方便对象的创建定义无参构造方法可以方便对象的创建不需要在创建对象时传入参数。这在某些场景下非常有用例如创建一个默认配置的对象或者创建一个对象池。

需要注意的是如果一个类没有定义构造方法Java会提供默认的无参构造方法如果一个类定义了有参构造方法但是没有定义无参构造方法那么在创建对象时必须传入参数否则编译器会报错。因此无论是否需要在构造方法中进行初始化操作都建议定义一个无参构造方法以便在需要时使用。

在调用子类构造方法之前会先调用父类没有参数的构造方法其目的是

在Java中如果一个类没有定义任何构造方法Java会提供默认的无参构造方法。如果一个类定义了构造方法但是没有定义无参构造方法那么在创建对象时必须调用有参构造方法并传入必要的参数。如果一个子类没有显式地调用父类的构造方法那么编译器会自动在子类的构造方法中插入一条调用父类无参构造方法的语句即super()。

调用父类无参构造方法的目的是确保父类的成员变量在子类对象创建之前得到了初始化。如果父类没有定义无参构造方法而子类又没有显式地调用父类的有参构造方法那么编译器就会报错。因此如果父类定义了有参构造方法那么子类必须显式地调用父类的有参构造方法以确保父类的成员变量得到了初始化。

总之调用父类无参构造方法的目的是确保父类的成员变量在子类对象创建之前得到了初始化如果父类没有定义无参构造方法那么子类必须显式地调用父类的有参构造方法。

一个类的构造方法的作用是什么若一个类没有声明构造方法程序能正确执行吗为什么

一个类的构造方法的作用是什么

一个类的构造方法的作用是在创建对象时对对象进行初始化可以用来初始化对象的成员变量确保对象在使用之前具有合理的状态。

骚戴理解对于合理的状态的理解如下

在Java中一个对象的状态是由其成员变量的值决定的。一个对象处于合理的状态意味着它的成员变量具有合理的值能够正常地执行其设计的功能。例如一个Person类的对象在创建时应该具有合理的姓名、年龄、性别等成员变量的值以便在程序中正确地使用这些信息。

构造方法的作用之一就是确保对象在创建时处于合理的状态。通过在构造方法中对成员变量进行初始化可以确保对象在使用之前具有合理的状态避免出现未初始化或不合理的状态。例如如果一个Person类的对象没有初始化姓名、年龄、性别等成员变量那么在程序中使用这些信息时就会出现错误导致程序崩溃或产生不正确的结果。

因此一个类的构造方法的作用是确保对象在创建时处于合理的状态即其成员变量具有合理的值能够正常地执行其设计的功能。

若一个类没有声明构造方法程序能正确执行吗为什么

可以执行。因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。

构造方法有哪些特性

Java中的构造方法有以下几个特性

1. 构造方法的名称必须与类名相同且没有返回类型包括void类型。

2. 构造方法可以有不同的参数列表包括无参构造方法、有参构造方法以及重载构造方法等。

3. 如果一个类没有定义任何构造方法Java会提供默认的无参构造方法如果定义了构造方法但没有定义无参构造方法那么在创建对象时必须调用有参构造方法并传入必要的参数。

4. 构造方法在创建对象时自动调用用于初始化对象的成员变量确保对象在使用之前具有合理的状态。

5. 构造方法可以调用其他方法或父类的构造方法并可以使用this和super关键字来访问当前对象和父类对象。

6. 如果一个子类没有显式地调用父类的构造方法那么编译器会自动在子类的构造方法中插入一条调用父类无参构造方法的语句即super()。

7. 如果一个类定义了有参构造方法那么子类必须显式地调用父类的有参构造方法以确保父类的成员变量得到了初始化。

总之构造方法是一种特殊的方法用于在创建对象时对对象进行初始化。构造方法的名称必须与类名相同没有返回类型可以有不同的参数列表。构造方法可以调用其他方法或父类的构造方法并可以使用this和super关键字来访问当前对象和父类对象。在使用构造方法时需要注意调用顺序和传入参数的正确性以确保对象在使用之前处于合理的状态。

骚戴理解这里我一直以为构造器只能是public修饰这是错误的

静态变量和实例变量(普通变量)区别

静态变量 静态变量由于不属于任何实例对象属于类的所以在内存中只会有一份在类的加载过程中JVM只为静态变量分配一次内存空间。

实例变量(普通变量) 每次创建对象都会为每个对象分配成员变量内存空间实例变量是属于实例对象的在内存中创建几次对象就有几份成员变量。

静态方法和实例方法有何不同

1、在外部调用静态方法时可以使用"类名.方法名"的方式也可以使用"对象名.方法名"的方式。而实例方法只有 后面这种方式。也就是说调用静态方法可以无需创建对象。

2、静态方法在访问本类的成员时只允许访问静态成员即静态成员变量和静态方法实例方法则无此限制

在一个静态方法内调用一个非静态成员为什么是非法的

在一个静态方法内调用一个非静态成员是非法的因为静态方法和非静态成员是属于不同的类成员它们的访问方式和作用域不同。

静态方法是属于类的方法它不依赖于任何对象的实例可以直接通过类名调用。静态方法只能访问静态成员因为静态成员在类加载时已经被初始化不依赖于对象的实例。

非静态成员是属于对象的成员它必须通过对象的实例来访问。非静态成员只能在对象创建后才能被访问因为它们的值是与对象的实例相关的。

因此在一个静态方法内调用一个非静态成员是非法的因为静态方法不依赖于对象的实例不能直接访问非静态成员。如果需要在静态方法中访问非静态成员可以通过创建对象的实例来访问或者将非静态成员改为静态成员。

什么是方法的返回值返回值的作用是什么

方法的返回值是指我们获取到的某个方法体中的代码执行后产生的结果前提是该方法可能产生结果。

返回值的作用:接收方法产生的结果使得它可以用于其他的操作

什么是内部类

在Java中可以将一个类的定义放在另外一个类的定义内部这就是内部类。

内部类本身就是类的一个属性与其他属性定义方式一致。

内部类的分类有哪些

Java中的内部类可以分为以下四种

1. 成员内部类Member Inner Class成员内部类是定义在另一个类中的类它可以访问外部类的所有成员变量和方法包括私有成员。成员内部类可以被声明为static或非static如果声明为static则称为静态内部类。

2. 局部内部类Local Inner Class局部内部类是定义在方法或作用域内的类它只能在该方法或作用域内访问不能被其他方法或作用域访问。局部内部类可以访问外部类的所有成员变量和方法但是只能访问final或effectively final的局部变量。

3. 匿名内部类Anonymous Inner Class匿名内部类是没有类名的内部类它通常用于创建实现了某个接口或继承了某个类的对象。匿名内部类可以访问外部类的所有成员变量和方法但是不能定义构造方法因为它没有类名。

4. 静态内部类Static Inner Class静态内部类是定义在另一个类中的static类它不能访问外部类的非static成员只能访问外部类的static成员。静态内部类可以被直接访问不需要先创建外部类的对象。

1、静态内部类

定义在类内部的静态类就是静态内部类。

public class Outer {
      private static int radius =1;
       //静态内部类
      static class StaticInner{ 
        public void visit(){
	        System.out.println("visit outer static variable:"+ radius);
        }
      }
}

静态内部类可以访问外部类所有的静态变量但是不可访问外部类的非静态变量

静态内部类的创建方式new 外部类.静态内部类()如下

Outer.StaticInner inner =new Outer.StaticInner();

inner.visit();

骚戴理解静态内部类里面定义的方法不一定都是静态方法。静态内部类是一个独立的类它可以包含静态成员和非静态成员也可以包含静态方法和非静态方法

2、成员内部类

定义在类内部成员位置上的非静态类就是成员内部类。

public class Outer {
    private static int radius =1;
    privateint count =2;
    class Inner{
        public void visit(){	
            System.out.println("visit outer static  variable:"+ radius);	
            System.out.println("visit outer variable:"+ count);
      }
   }
}

成员内部类可以访问外部类所有的变量和方法包括静态和非静态私有和公有。

成员内部类依赖于外部类的实例它的创建方式外部类实例.new 内部类()如下

Outer outer =new Outer();

Outer.Inner inner = outer.new Inner();

inner.visit();

骚戴理解可以看出静态内部类和成员内部类在定义方面只是差了一个static

3、局部内部类

定义在方法中的内部类就是局部内部类。

public class Outer{
    private int out_a =1;
    private static int STATIC_b =2;
    public void test FunctionClass(){
        int inner_c=3;
        class Inner{
            private void fun(){		
                System.out.println(out_a);	
                System.out.println(STATIC_b); 
                System.out.println(inner_c);
                }
             }	
                Inner  inner =new Inner();	//直接在方法里用new创建局部内部类
                inner.fun();
           }

        public static void test StaticFunctionClass(){
            int d=3;
            class Inner{
                private void fun(){
                    //System.out.println(out_a); 
                    //编译错误定义在静态方法中的局部类不可以访问外部类的实例变量
                    System.out.println(STATIC_b);	
                    System.out.println(d);
                    }
                }	
                    Inner  inner =new Inner();	//直接在方法里用new创建局部内部类
                    inner.fun();
      }
} 

定义在实例方法中的局部类可以访问外部类的所有变量和方法定义在静态方法中的局部类只能访问外部类的静态变量和静态方法 。

局部内部类的创建方式 在对应方法内 new 内部类() 如下

public static void testStaticFunctionClass(){
  class Inner{} 
  Inner inner =new Inner();
}

4、匿名内部类

匿名内部类就是没有名字的内部类

匿名内部类举例

红色框画出来的就是一个匿名内部类同时重写了父类Animals的eat方法并且调用了这个匿名内部类的eat方法

如何调用匿名内部类中的方法

1、匿名内部类中只有一个方法的情况

2、匿名内部类有多个方法

第一种方式

第二种方式

如果想调用匿名内部类自己特有的方法的时候呢该如何调用呢

骚戴理解简单来说就是这个实现不了匿名内部类无法调用自己特有的接口直接调用会报错它只能调用继承的类或实现的接口里有的方法例如上面的run方法就是上面匿名内部类特有的方法这样的方法是无法调用的

匿名内部类实现接口

匿名内部类存在的前提是要有继承或者实现关系的但是并没有看到extends和implements关键字这是怎么回事呢

答在使用匿名内部类时确实需要有继承或者实现关系但是并不需要使用extends和implements关键字来显示地声明继承或实现关系。这是因为在匿名内部类的定义中已经隐含了继承或实现的关系。这些由jvm搞定了。

匿名内部类的特点

除了没有名字匿名内部类还有以下特点

1、匿名内部类必须继承一个抽象类或者实现一个接口。

2、匿名内部类不能定义任何静态成员和静态方法。

3、匿名内部类访问局部变量的时候必须把局部变量声明为 final。

4、匿名内部类不能是抽象的它必须要实现继承的类或者实现的接口的所有抽象方法。

匿名内部类创建方式

new 类/接口{

    //匿名内部类实现部分

}

 内部类的优点

内部类是Java中一种特殊的类它定义在另一个类的内部。内部类具有以下几个优点

1. 封装性内部类可以访问外部类的私有成员而外部类不能访问内部类的私有成员。这样可以实现更好的封装性防止外部类直接访问内部类的私有成员。

2. 继承性内部类可以继承外部类而且可以多重继承。这样可以实现更灵活的继承关系

3. 多态性内部类可以实现接口或继承抽象类从而实现多态性。这样可以更好地支持面向对象编程的特性如封装、继承和多态。

4. 代码组织性内部类可以将相关的代码组织在一起从而提高代码的可读性和可维护性。内部类可以访问外部类的成员从而可以更方便地实现某些功能。

5. 回调函数内部类可以用作回调函数从而实现更灵活的事件处理机制。内部类可以访问外部类的成员从而可以更方便地处理事件。

总之内部类是Java中一种非常有用的特性它可以提高代码的封装性、继承性、多态性、代码组织性和灵活性可以帮助我们更好地实现面向对象编程的特性。

内部类有哪些应用场景 

内部类是Java中一种特殊的类它定义在另一个类的内部。内部类具有以下几个应用场景

1. 实现回调函数内部类可以用作回调函数从而实现更灵活的事件处理机制。内部类可以访问外部类的成员从而可以更方便地处理事件。

2. 实现多重继承内部类可以继承外部类而且可以多重继承。这样可以实现更灵活的继承关系可以避免多重继承的一些问题。

3. 实现接口内部类可以实现接口或继承抽象类从而实现多态性。这样可以更好地支持面向对象编程的特性如封装、继承和多态。

4. 封装性内部类可以访问外部类的私有成员而外部类不能访问内部类的私有成员。这样可以实现更好的封装性防止外部类直接访问内部类的私有成员。

5. 代码组织性内部类可以将相关的代码组织在一起从而提高代码的可读性和可维护性。内部类可以访问外部类的成员从而可以更方便地实现某些功能。

6. 实现迭代器内部类可以用于实现迭代器从而实现更灵活的遍历方式。内部类可以访问外部类的成员从而可以更方便地实现迭代器的功能。

总之内部类是Java中一种非常有用的特性它可以提高代码的封装性、继承性、多态性、代码组织性和灵活性可以帮助我们更好地实现面向对象编程的特性。内部类在实际开发中有着广泛的应用场景。

局部内部类和匿名内部类访问局部变量的时候为什么变量必须要加上final 

在Java中局部内部类和匿名内部类访问局部变量时如果这些变量没有被声明为final编译器会报错。这是因为Java要求在内部类中访问局部变量时这些变量必须是final或者是实际上的final。

这是因为内部类和外部类是两个独立的作用域内部类可以访问外部类的成员变量和方法但是不能直接访问外部类的局部变量。为了让内部类能够访问外部类的局部变量Java采用了将这些变量复制一份到内部类中的方式。但是如果这些变量不是final或实际上的final那么在复制的过程中这些变量的值可能会被修改从而导致内部类和外部类的值不一致。

为了避免这种情况的发生Java要求在内部类中访问局部变量时这些变量必须是final或者是实际上的final。这样在复制变量的值到内部类中时就可以保证这些变量的值不会被修改从而保证内部类和外部类的值一致。

需要注意的是Java8之后如果变量是final或者是实际上的final那么就不需要显式地声明为final编译器会自动将它们视为final变量。但是如果变量被修改了编译器仍然会报错。

总之Java要求在内部类中访问局部变量时这些变量必须是final或者是实际上的final这是为了保证内部类和外部类的值一致。

匿名内部类为什么不能定义任何静态成员和静态方法

匿名内部类是一种没有名字的内部类它是在创建对象时动态生成的一个类。因为匿名内部类没有名字所以它无法被其他类所引用也无法被继承或扩展。

静态成员和静态方法是属于类的成员它们在类加载时就已经被初始化而不是在对象创建时才被初始化。由于匿名内部类没有名字它在类加载时无法被初始化静态成员和静态方法因此不能定义任何静态成员和静态方法。

可以这样理解静态成员和静态方法是属于类的而匿名内部类是属于对象的它只是在创建对象时动态生成的一个类。因此匿名内部类不能定义静态成员和静态方法就好像一个人没有名字就无法拥有自己的财产一样。

总之匿名内部类是一种没有名字的内部类它无法被其他类所引用也无法被继承或扩展。由于它没有名字无法在类加载时被初始化静态成员和静态方法因此不能定义任何静态成员和静态方法。

构造器constructor是否可被重写override 

构造器constructor不是普通的方法因此不能像方法一样被重写override。构造器是用来创建对象的特殊方法它在创建对象时被调用用来初始化对象的成员变量。每个类都有自己的构造器如果没有显式地定义构造器Java会自动生成一个默认的构造器。

虽然构造器不能被重写但是可以通过继承来实现构造器的复用。子类可以使用super关键字调用父类的构造器从而实现对父类构造器的重用。如果子类没有显式地调用父类的构造器Java会默认调用父类的无参构造器。如果父类没有无参构造器子类必须显式地调用父类的带参构造器。

需要注意的是子类的构造器必须先调用父类的构造器然后再进行自己的初始化操作。如果子类没有显式地调用父类的构造器Java会默认调用父类的无参构造器如果父类没有无参构造器编译会出错。因此在使用继承时需要注意构造器的调用顺序和参数传递问题。

重载Overload和重写Override的区别 

重载Overload和重写Override是Java中两个重要的概念它们的区别如下

1. 定义重载指在同一个类中定义多个同名但参数类型或个数不同的方法重写指在子类中重新定义父类中已有的方法。

2. 参数重载方法的参数类型或个数必须不同否则会出现编译错误重写方法的参数类型和个数必须与父类中被重写的方法相同。

3. 返回值重载方法的返回值类型可以相同也可以不同但是不能只有返回类型不同重写方法的返回值类型必须与父类中被重写的方法相同或是其子类。

4. 访问修饰符重载方法可以具有不同的访问修饰符但是不能比父类中被重载的方法的访问修饰符更严格重写方法的访问修饰符必须比父类中被重写的方法的访问修饰符更宽松。

5. 静态方法重载可以包括静态方法但是不能只有静态性质不同重写方法不能是静态方法。

6. 父类和子类重载方法必须在同一个类中定义重写方法必须在子类中定义。

重载的方法能否根据返回类型进行区分 

在Java中重载的方法不能仅根据返回类型进行区分。这是因为Java编译器在重载方法时只会考虑方法名和参数列表而不会考虑返回类型。如果两个方法的方法名和参数列表相同但是返回类型不同那么编译器会认为这是两个相同的方法从而导致编译错误。

例如下面的代码就会导致编译错误

```
public class Test {
    public void foo() {}
    public int foo() { return 0; }
}
```

这是因为上面的代码中定义了两个同名、参数列表相同但返回类型不同的方法编译器无法区分它们从而导致编译错误。

== 和 equals 的区别是什么 

== : 它的作用是判断两个对象的地址是不是相等。即判断两个对象是不是同一个对象。(基本数据类型 == 比较的是值引用数据类型 == 比较的是内存地址)

equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况

  • 情况1如果一个类没有重写“equals”方法那么它将继承Object类的“equals”方法该方法比较的是两个对象的内存地址是否相同即与“==”比较的结果相同
  • 情况2如果重写了equals方法那么就是比较内容是否相同例如String类的equals方法就是重写了的

举个例子

public class test1{
    public static void main(String[] args){	
    String a =new String("ab");// a 为一个引用	
    String b=new String("ab");// b为另一个引用,对象的内容一样	
    String aa ="ab";// 放在常量池中	
    String bb ="ab";// 从常量池中查找
    if(aa == bb)// true	
    System.out.println("aa==bb");
    if(a == b)// false非同一对象
    System.out.println("a==b");
    if(a.equals(b))// true		
    System.out.println("aEQb");
    if(42==42.0){// true
     System.out.println("true");
      }
   }
}

骚戴理解String中的equals方法是被重写过的所以String的equals方法比较的是对象的内容是否相同。

java中常量放在jvm的堆还是方法区

在Java中常量一般放在方法区中而不是放在JVM的堆中。方法区是一块用于存储类的元数据、静态变量、常量、类信息等的内存区域它是线程共享的与堆一样也是被整个JVM所共享的。

具体来说Java中的常量包括两种类型字面常量和符号常量。字面常量是指代码中直接使用的常量值比如字符串、数字、布尔值等而符号常量是指使用final关键字定义的常量它们的值在编译时就已经确定并且不能被修改。

对于字面常量它们通常是放在JVM的常量池中的常量池是方法区的一部分用于存储编译时生成的各种字面常量和符号引用。常量池中的常量可以被多个线程共享而且它们的生命周期与类的生命周期相同。

对于符号常量它们通常是放在方法区的静态变量中的也就是说它们的值在类加载时就已经确定并且在整个程序的生命周期内都不会被修改。

总之在Java中常量一般放在方法区中常量池和静态变量都是方法区的一部分用于存储常量和静态变量等信息。

hashCode 与 equals

1、hashCode()介绍

hashCode()的作用是获取哈希码也称为散列码它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中这就意味着Java中的任何类都包含有hashCode()函数。散列表存储的是键值对(key-value)它的特点是能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码可以快速找到所需要的对象

2、为什么要有 hashCode

hashCode是一个用于快速查找对象的整数值它可以用于哈希表、集合等数据结构中以提高数据的查找效率。hashCode方法是一种散列码算法它可以将任意长度的输入数据映射到固定长度的散列码中从而方便对数据进行比较和查找

例如当你把对象加入 HashSet 时HashSet 会先计算对象的 hashcode 值来判断对象加入的位置同时也会与其他已经加入的对象的 hashcode值作比较如果没有相符的hashcodeHashSet会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象这时会调用 equals()方法来检查 hashcode 相等的对象是否真的相同。如果两者相同 HashSet 就不会让其加入操作成功。如果不同的话就会重新散列到其他位置。这样我们就大大减少了 equals 的次数相应就大大提高了执行速度。

3、两个对象的 hashCode() 相同则 equals() 也一定为 true对吗

1、如果两个对象相等则hashcode一定也是相同的

2、两个对象相等对两个对象调用equals方法返回true

3、两个对象有相同的hashcode值它们也不一定是相等的

4、为什么重写equals时必须重写hashCode方法

如果只重写了 equals 方法那么默认情况下假设存了两个自定义的内容相同的对象到Set中Set 进行去重操作时会先判断两个对象的 hashCode 是否相同此时因为没有重写 hashCode 方法所以会直接执行 Object 中的 hashCode 方法而 Object 中的 hashCode 方法对比的是两个不同引用地址的对象那么得到的两个Hash值是不一样的那么 equals 方法就不会执行了这两个对象就会被判断为不是相等的于是就在 Set 集合中插入了两个相同的对象这里的相同是指的内容相同。

但是如果在重写 equals 方法时也重写了 hashCode 方法那么在执行判断时会去执行重写的 hashCode 方法此时对比的是两个对象的所有属性的 hashCode 是否相同于是调用 hashCode 返回的结果就是 true再去调用 equals 方法发现两个对象确实是相等的于是就返回 true 了因此 Set 集合就不会存储两个一模一样的数据了于是整个程序的执行就正常了。

总结

hashCode 和 equals 两个方法是用来协同判断两个对象是否相等的采用这种方式的原因是可以提高程序插入和查询的速度如果在重写 equals 时不重写 hashCode就会导致在某些场景下例如将两个相等的自定义对象存储在 Set 集合时就会出现程序执行的异常为了保证程序的正常执行所以我们就需要在重写 equals 时也一并重写 hashCode 方法才行。

对象的相等与指向他们的引用相等两者有什么不同 

在Java中对象的相等与指向它们的引用相等是两个不同的概念它们有以下几点不同

1. 比较方式不同对象的相等通常使用equals方法进行比较而指向它们的引用相等通常使用“==”运算符进行比较。

2. 比较结果不同对象的相等比较的是对象的内容是否相同而指向它们的引用相等比较的是对象的内存地址是否相同。

3. 对象类型不同对象的相等适用于所有对象类型包括基本数据类型和对象类型而指向它们的引用相等只适用于对象类型。

需要注意的是如果一个类没有重载equals方法那么它将继承Object类的equals方法该方法比较的是两个对象的内存地址是否相同即与指向它们的引用相等的结果相同。

通俗易懂的讲解为什么 Java 中只有值传递 

在Java中只有值传递这是因为Java中的变量在传递给方法时实际上是将变量的值即数据的拷贝传递给方法而不是将变量本身传递给方法。

可以将变量想象成一个盒子盒子里装着数据。当一个变量作为参数传递给方法时实际上是将盒子里的数据复制一份并将这份数据传递给方法。这就好比你把一个盒子里的东西复制一份给朋友朋友拿到的是复制品而不是原来的盒子。同理方法也拿到的是变量的复制品而不是原来的变量。

需要注意的是如果变量是引用类型的变量那么复制的是变量的值即内存地址而不是对象本身。这意味着如果在方法内部修改了对象的数据那么在方法外部也能看到这些修改因为它们都是指向同一个对象的。

总之Java中只有值传递因为变量在传递给方法时实际上是将变量的值即数据的拷贝传递给方法而不是将变量本身传递给方法。因此在Java中不能直接实现引用传递但是可以通过修改对象的数据来达到类似于引用传递的效果。

骚戴理解java中只要值传递你传递的变量如果是引用类型那也是把这个变量的内存地址复制一份传过去重点就在于这里是复制虽然我传递的是内存地址但是我采用的方法是复制这个内存地址再把复制的传过去效果是和引用传递一样但是从定义上来看由于是复制一份内存地址传递所以就是值传递

值传递和引用传递有什么区别 

值传递和引用传递是两种不同的参数传递方式它们的区别主要在于传递的是变量的值还是变量的引用内存地址。

在值传递中当一个变量作为参数传递给方法时实际上是将该变量的值即数据的拷贝传递给方法而不是将变量本身传递给方法。在方法内部对该变量进行修改不会影响到原来的变量因为它们指向的是不同的内存地址。这就好比你把一个盒子复制一份给朋友朋友拿到的是复制品而不是原来的盒子。

在引用传递中当一个变量作为参数传递给方法时实际上是将该变量的引用内存地址传递给方法而不是将变量的值传递给方法。在方法内部对该变量进行修改会影响到原来的变量因为它们指向的是同一个内存地址。这就好比你把一个盒子里的东西给朋友朋友拿到的是原来的盒子你和朋友都可以修改盒子里的东西。

JDK 中常用的包有哪些 

JDK中常用的包有很多其中一些常用的包如下

1. java.lang包含Java语言的核心类如基本数据类型、字符串、异常处理等。

2. java.util包含各种实用工具类如集合框架、日期和时间处理、随机数生成、正则表达式等。

3. java.io包含输入输出相关的类如文件读写、网络通信、对象序列化等。

4. java.net包含网络编程相关的类如Socket、URL等。

5. java.awt包含图形用户界面GUI相关的类如窗口、按钮、文本框等。

6. javax.swing是AWT的扩展提供了更多的GUI组件、更好的外观和感觉、更好的事件处理等。

7. java.sql包含与数据库相关的类如连接数据库、执行SQL语句、处理结果集等。

8. java.math包含高精度计算相关的类如BigDecimal、BigInteger等。

9. java.security包含与安全相关的类如加密、数字签名、密钥管理等。

10. java.text包含文本处理相关的类如格式化和解析日期、数字、货币等。

除了以上这些常用的包之外JDK中还有很多其他的包如XML处理、国际化、反射等。开发者在编写Java程序时需要根据具体的需求选择合适的包

IO流

反射

什么是反射机制 

反射是Java语言的一种机制它允许程序在运行时获取对象的类型信息、访问对象的属性和方法并调用对象的方法。反射机制使得程序可以在运行时动态地创建对象、调用方法、访问属性等而不需要在编译时确定这些信息。

在Java中每个类都有一个Class对象它包含了该类的所有信息如类名、属性、方法等。通过反射机制可以获取一个类的Class对象然后使用该对象来访问类的属性和方法。反射机制可以让程序在运行时动态地创建对象、调用方法、访问属性等而不需要在编译时确定这些信息。

反射机制在Java中有很多应用如动态代理、注解处理、框架开发等。但是由于反射机制需要在运行时动态地获取对象信息所以会带来一定的性能损失。因此在使用反射机制时需要注意性能问题并合理使用缓存等技术来提高程序的效率。

反射机制优缺点

反射机制是Java语言的一种特性它允许程序在运行时动态地获取对象的类型信息、访问对象的属性和方法并调用对象的方法。反射机制的优缺点如下

优点

1. 动态性反射机制允许程序在运行时动态地获取对象的类型信息、访问对象的属性和方法并调用对象的方法从而使程序具有更大的灵活性和动态性。

2. 扩展性反射机制可以让程序在运行时动态地创建对象、调用方法、访问属性等而不需要在编译时确定这些信息从而使程序具有更好的扩展性。

3. 适用性反射机制可以应用于很多场景如动态代理、注解处理、框架开发等使程序更加灵活和可扩展。

缺点

1. 性能问题反射机制需要在运行时动态地获取对象信息所以会带来一定的性能损失特别是在频繁调用的情况下会对程序的性能产生较大的影响。

2. 安全问题反射机制可以访问对象的私有属性和方法从而可能破坏程序的安全性。

3. 可读性问题反射机制使得程序的逻辑更加复杂代码可读性较差特别是对于初学者来说可能会造成困难。

总之反射机制是Java语言的一种特性它具有动态性、扩展性和适用性等优点但也存在性能、安全和可读性等缺点。在使用反射机制时需要权衡其优缺点并根据具体情况进行选择。

反射机制的应用场景有哪些

反射机制在Java中有很多应用场景包括以下几个方面

1. 动态代理通过反射机制可以动态地生成代理对象从而实现对目标对象的代理操作如AOP、RPC等。

2. 注解处理通过反射机制可以读取和处理注解信息从而实现对程序的自动化处理如自动化测试、代码生成等。

3. 框架开发通过反射机制可以实现框架的扩展性和灵活性如Spring框架中的BeanFactory、AOP、IOC等。Spring 通过 XML 配置模式装载 Bean 的过程根据配置文件里的bean定义通过反射创建一个bean对象

4. 反射调试工具通过反射机制可以实现反射调试工具如获取对象的类名、属性、方法等信息从而方便程序员进行调试和开发。我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序

5. 序列化和反序列化通过反射机制可以实现对象的序列化和反序列化从而实现对象的持久化和传输。

6. 动态加载类和资源通过反射机制可以动态地加载类和资源从而实现程序的动态扩展和更新。

Java获取反射的三种方法

  • 通过对象实例的getClass方法获取 (使用的最少的)
  • 通过Class.forName"路径"实现反射机制 jdbc驱动
  • 通过类名.class来实现反射机制
public class Student{
    private int id;    
    String name;
    protected boolean sex;
    public float score;
    } 
public classGet{
    //获取反射机制三种方式
    public static void main(String[] args) throws ClassNotFoundException {
    //方式一(通过建立对象)	
    Student stu =new  Student();
    Class classobj1= stu.getClass();	
    System.out.println(classobj1.getName());
    //方式二(所在通过路径-相对路径	
    Class classobj2= Class.forName("fanshe.Student");                     
    System.out.println(classobj2.getName());
    //方式三通过类名	
    Class classobj3= Student.class;
    System.out.println(classobj3.getName());
    }
}

String 

字符型常量和字符串常量的区别 

字符型常量和字符串常量是Java中的两种常量类型它们的区别如下

1. 字符型常量字符型常量是用单引号括起来的单个字符如'a'、'b'、'c'等。字符型常量只能包含一个字符且必须用单引号括起来。

2. 字符串常量字符串常量是用双引号括起来的一串字符如"Hello"、"World"等。字符串常量可以包含多个字符且必须用双引号括起来。

3. 存储方式字符型常量在内存中以字符的形式存储而字符串常量在内存中以字符数组的形式存储。

4. 可变性字符型常量是不可变的一旦定义就不能修改。而字符串常量可以通过各种方法进行修改如拼接、替换、截取等。

5. 比较方式字符型常量可以使用==运算符进行比较比较的是字符的ASCII码值。而字符串常量不能使用==运算符进行比较需要使用equals()方法或compareTo()方法进行比较。

什么是字符串常量池 

字符串常量池是Java中的一种特殊的内存区域用于存储字符串常量以便重复使用。当程序创建一个字符串常量时如果该字符串常量在常量池中已经存在则直接返回该字符串常量的引用如果该字符串常量在常量池中不存在则将该字符串常量添加到常量池中并返回该字符串常量的引用。

字符串常量池的优点是可以减少内存的使用提高程序的性能。因为字符串常量池会缓存字符串常量以便重复使用所以可以避免创建重复的字符串常量从而减少内存的使用。同时由于字符串常量池是一种特殊的内存区域它的访问速度比堆内存和栈内存更快所以可以提高程序的性能。

需要注意的是字符串常量池中的字符串常量是不可变的一旦定义就不能修改。如果程序需要修改字符串常量应该使用StringBuilder或StringBuffer等可变的字符串类型。

jdk1.8的字符串常量池是在堆内存中吗 

在JDK1.8中字符串常量池的位置是在堆内存中但是和一般的堆内存不同它是被划分出来的一块特殊的内存区域称为"永久代"Permanent Generation也叫"元空间"Metaspace。

在JDK1.8之前字符串常量池是在方法区中的但是在JDK1.8之后方法区被移除了取而代之的是Metaspace。Metaspace是一种与堆内存分离的内存区域用于存储类的元数据、静态变量、常量等数据。在Metaspace中字符串常量池也被移动到了堆内存中。

需要注意的是在JDK1.8中字符串常量池也有一些变化。在JDK1.8之前字符串常量池是固定大小的当常量池中的字符串对象过多时会导致OutOfMemoryError错误。但是在JDK1.8中字符串常量池的大小是不固定的可以动态调整避免了OutOfMemoryError错误的问题。同时JDK1.8中也引入了一种新的字符串常量池实现方式称为"字符串去重"String Deduplication可以进一步减少字符串常量池的内存占用。

String 是最基本的数据类型吗 

不是。Java 中的基本数据类型只有 8 个 byte、short、int、long、float、double、char、boolean除了基本类型primitive type剩下的都是引用类型referencetypeJava 5 以后引入的枚举类型也算是一种比较特殊的引用类型。

这是很基础的东西但是很多初学者却容易忽视Java 的 8 种基本数据类型中不包括 String基本数据类型中用来描述文本数据的是 char但是它只能表示单个字符比如 ‘a’,‘好’ 之类的如果要描述一段文本就需要用多个char 类型的变量也就是一个 char 类型数组比如“你好” 就是长度为2的数组 char[] chars = {‘你’,‘好’};

但是使用数组过于麻烦所以就有了 StringString 底层就是一个 char 类型的数组只是使用的时候开发者不需要直接操作底层数组用更加简便的方式即可完成对字符串的使用。

简单来说就是String类利用了final修饰的char类型数组存储字符源码如下所示

private final char value[];

String有哪些特性 

String是Java中的一个类具有以下特性

1. 不可变性String对象一旦创建就不能被修改。如果需要修改字符串内容只能创建一个新的String对象。这种不可变性使得String对象在多线程环境下更加安全。

2. 字符串常量池Java中的字符串常量池是一种特殊的内存区域用于缓存字符串常量以便重复使用。String类中的字符串常量都是在字符串常量池中创建的。

3. 比较方式String类中提供了多种比较字符串的方法如equals()、equalsIgnoreCase()、compareTo()等。其中equals()方法比较的是字符串的内容而==运算符比较的是字符串对象的引用。

4. 不可变哈希值String类中的哈希值是不可变的一旦计算出来就不能被修改。这种不可变性使得String对象可以作为Map的key来使用。

5. 字符串操作方法String类中提供了多种字符串操作方法如substring()、trim()、toUpperCase()、toLowerCase()等可以方便地对字符串进行操作和处理。

6. 国际化支持String类中提供了多种支持国际化的方法如getBytes()、charAt()、length()等可以处理不同语言和编码的字符串。

String为什么是不可变的吗

String是不可变的主要是为了保证程序的安全性和性能。

1. 安全性如果String是可变的那么在多线程环境下多个线程可能会同时修改同一个String对象从而导致数据不一致的问题。为了避免这种问题Java将String设计为不可变的一旦创建就不能被修改这样就可以避免多线程环境下的数据竞争问题。

2. 性能如果String是可变的那么每次修改字符串都需要创建一个新的String对象这样会导致频繁的对象创建和销毁从而降低程序的性能。为了避免这种问题Java将String设计为不可变的一旦创建就不需要再修改这样可以避免频繁的对象创建和销毁提高程序的性能。

另外String是不可变的还有一个好处就是可以作为Map的key来使用。由于String的哈希值是不可变的一旦计算出来就不能被修改所以String对象可以作为Map的key来使用保证了Map的稳定性和正确性。如果String是可变的就不能作为Map的key来使用从而限制了Map的使用范围。

综上所述String是不可变的主要是为了保证程序的安全性和性能。

是否可以继承 String 类 

String 类是 final 类不可以被继承。

String str="i"与 String str=new String(“i”)一样吗 

在功能上String str="i"与String str=new String("i")是相同的都是创建一个包含单个字符i的字符串对象。但是在内存中它们是不同的。

String str="i"是使用字符串常量池中的字符串常量创建的而字符串常量池是Java中的一种特殊的内存区域用于缓存字符串常量以便重复使用。当程序创建一个字符串常量时如果该字符串常量在常量池中已经存在则直接返回该字符串常量的引用如果该字符串常量在常量池中不存在则将该字符串常量添加到常量池中并返回该字符串常量的引用。因此使用String str="i"创建的字符串对象会被添加到字符串常量池中如果创建多个包含相同内容的字符串对象只会在字符串常量池中创建一个对象可以节省内存空间。

而String str=new String("i")是使用new关键字创建的字符串对象每次都会在堆内存中创建一个新的对象不会使用字符串常量池中的对象。如果创建多个包含相同内容的字符串对象每个对象都会在堆内存中创建一个新的对象会占用更多的内存空间。

因此如果需要创建包含单个字符的字符串对象建议使用String str="i"的方式可以避免在堆内存中创建多个相同的对象节省内存空间。

String s = new String(“xyz”);创建了几个字符串对象

使用String s = new String("xyz")创建了两个字符串对象。

第一个字符串对象是字面量"xyz"它是在编译期间就被创建并存储在字符串常量池中的。当程序执行到String s = new String("xyz")这句话时会在堆内存中创建一个新的String对象该对象的内容是字符串常量池中"xyz"字符串对象的拷贝。因此使用new关键字创建String对象时会在堆内存中创建一个新的对象。

总之使用String s = new String("xyz")创建了两个字符串对象一个是字符串常量池中的"xyz"字符串对象另一个是堆内存中的新对象该对象的内容是字符串常量池中"xyz"字符串对象的拷贝。需要注意的是如果在代码中多次使用相同的字符串字面量只会在字符串常量池中创建一个字符串对象不会重复创建。

String str1 ="hello";//str1指向静态区字符串常量池

String str2 =new String("hello");//str2指向堆上的对象

String str3 ="hello";

String str4=new String("hello");

1、System.out.println(str1.equals(str2));//true

2、System.out.println(str2.equals(str4));//true

3、System.out.println(str1== str3);//true

4、System.out.println(str1 == str2);//false

5、System.out.println(str2 == str4);//false

6、System.out.println(str2=="hello");//false

str2 = str1;

7、System.out.println(str2 =="hello");//true

骚戴理解首先String的equals方法是被重写过的比较的内容是否相同所以序号1和2都是true因为内容都是hello但是"=="在比较引用类型的时候比较的是内存地址是否相同因为str1和str3都是指向的字符串常量池里hello这个字符串的地址所以序号3位true但是序号4、5、6都是false因为它们内存地址都不一样str2 = str1把str2指向的地址改成了指向字符串常量池中hello的地址后自然序号7为true

如何将字符串反转

使用 StringBuilder 或者 stringBuffer 的 reverse() 方法。

示例代码

// StringBuffer reverse

StringBuffer stringBuffer = new StringBuffer();

stringBuffer. append("abcdefg");

System. out. println(stringBuffer. reverse());

结果 gfedcba

// StringBuilder reverse

StringBuilder stringBuilder = new StringBuilder();

stringBuilder. append("abcdefg");

System. out. println(stringBuilder. reverse());

结果 gfedcba

数组有没有 length()方法String 有没有 length()方法

数组没有length()方法但是有一个length属性可以用来获取数组的长度或者用 size方法。

例如对于int类型的数组可以使用以下方式获取数组的长度

int[] arr = {1, 2, 3, 4, 5};
int len = arr.length; // 获取数组的长度结果为5

对于String类型的对象有一个length()方法可以返回字符串的长度即字符串中字符的个数。

例如可以使用以下方式获取字符串的长度

String str = "Hello World";
int len = str.length(); // 获取字符串的长度结果为11

需要注意的是String的length()方法返回的是字符串中字符的个数而不是字节数或者编码单元数。对于包含Unicode字符的字符串其长度可能会大于字符串的字节数或编码单元数。

String 类的常用方法都有那些 

String类是Java中最常用的类之一提供了许多实用的方法下面列出了String类的一些常用方法

1. charAt(int index)返回指定索引处的字符。

2. length()返回字符串的长度即字符串中字符的个数。

3. substring(int beginIndex)返回从指定索引开始到字符串末尾的子字符串。

4. substring(int beginIndex, int endIndex)返回从指定索引开始到指定索引结束的子字符串。

5. equals(Object obj)比较字符串是否相等区分大小写。

6. equalsIgnoreCase(String anotherString)比较字符串是否相等忽略大小写。

7. compareTo(String anotherString)按字典顺序比较两个字符串如果相等返回0如果当前字符串大于另一个字符串返回正数否则返回负数。

8. toLowerCase()将字符串中的所有字符转换为小写。

9. toUpperCase()将字符串中的所有字符转换为大写。

10. trim()去除字符串开头和结尾的空格。

11. startsWith(String prefix)判断字符串是否以指定的前缀开始。

12. endsWith(String suffix)判断字符串是否以指定的后缀结束。

13. indexOf(int ch)返回指定字符在字符串中第一次出现的索引。

14. lastIndexOf(int ch)返回指定字符在字符串中最后一次出现的索引。

15. replace(char oldChar, char newChar)将字符串中的所有旧字符替换为新字符。

16. replaceAll(String regex, String replacement)将字符串中所有匹配正则表达式的子串替换为指定字符串。

17. split(String regex)按照指定的正则表达式将字符串拆分为多个子串。

18. valueOf(Object obj)将指定的对象转换为字符串。

19. format(String format, Object... args)使用指定的格式字符串和参数返回格式化的字符串。

在使用 HashMap 的时候用 String 做 key 有什么好处 

在使用HashMap时使用String作为key有以下好处

1. String是不可变的因此可以保证key的不可变性避免了在修改key时可能出现的问题。

2. String的hashCode()方法被重写过可以保证相同字符串的hashCode值相同因此可以更好地利用HashMap的散列特性。

3. String实现了Comparable接口可以进行比较因此在对HashMap进行排序或者遍历时可以更方便地进行操作。

4. String是Java中最常用的类之一使用String作为key可以提高代码的可读性和可维护性。

骚戴理解需要注意的是在使用自定义类作为HashMap的key时需要重写hashCode()和equals()方法以保证正确性和性能。

String和StringBuffer、StringBuilder的区别是什么 

String、StringBuffer和StringBuilder都是Java中用于处理字符串的类它们的主要区别如下

1. String是不可变的即一旦创建就不能修改对String对象进行修改实际上是创建了一个新的String对象旧的String对象仍然存在。而StringBuffer和StringBuilder是可变的可以修改其内容。

2. StringBuffer和StringBuilder都是可变的字符串缓冲区它们的主要区别在于线程安全性。StringBuffer是线程安全的即多个线程可以同时访问一个StringBuffer对象并执行修改操作而不会产生不一致的结果。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁所以是线程安全的。StringBuilder则是非线程安全的如果多个线程同时访问一个StringBuilder对象并执行修改操作可能会产生不一致的结果。String中的对象是不可变的也就可以理解为常量线程安全。

3. 在字符串拼接的场景中如果使用String进行拼接每次拼接都会创建一个新的String对象如果拼接的字符串比较多会产生大量的临时对象占用大量内存影响性能。而如果使用StringBuffer或StringBuilder进行拼接由于它们是可变的可以直接在原有对象上进行修改避免了创建大量临时对象提高了性能。

 骚戴理解性能由好到坏StringBuilder>StringBuffer>String

总之String、StringBuffer和StringBuilder都有各自的特点需要根据具体的场景选择合适的类进行使用。如果需要进行频繁的字符串拼接操作建议使用StringBuffer或StringBuilder而不是使用String。如果需要在多线程环境下进行字符串操作建议使用StringBuffer而不是使用StringBuilder。

包装类

自动装箱与拆箱

装箱将基本类型用它们对应的引用类型包装起来

拆箱将包装类型转换为基本数据类型

int 和 Integer 有什么区别 

它们的主要区别如下

1. int是Java的基本数据类型而Integer是int的包装类是一个对象。

2. int在内存中占用4个字节而Integer对象在内存中占用更多的空间。

3. int可以直接进行数值运算而Integer对象需要通过intValue()方法将其转换为int类型后才能进行数值运算。

4. int是值类型即在传递时传递的是实际的数值而Integer是引用类型即在传递时传递的是对象的引用。

5. 在使用时int通常用于基本的数值运算而Integer通常用于需要将int类型的数据封装成对象的场景例如在集合类中存储int类型的数据。

总之int和Integer都有各自的特点和使用场景需要根据具体的需求进行选择和使用。需要注意的是在进行数值运算时尽量使用int避免使用Integer对象以提高性能。

 Java 原始类型和包装类对比

Java 为每个原始类型提供了包装类型

原始类型:boolean char byte short int long float double

包装类型Boolean Character Byte Short Integer Long Float Double

Integer a= 127 与 Integer b = 127相等吗

相等

总结

  • int 和Integer在进行比较的时候Integer会进行拆箱转为int值与int进行比较。
  • Integer与Integer比较的时候由于直接赋值的时候会进行自动的装箱那么这里就需要注意两个问题
  1. 一是-128<= x<=127的整数那么当赋值在这个区间的时候不会创建新的Integer对象而是从IntegerCache缓存中获取已经创建好的Integer对象。
  2. 二是当大于这个范围的时候直接new Integer来创建Integer对象。
  • new Integer(1) 和Integer a = 1不同前者会创建对象存储在堆中而后者因为在-128到127的范围内不会创建新的对象而是从IntegerCache中获取的。那么Integer a = 128, 大于该范围的话才会直接通过new Integer128创建对象进行装箱。
  • 对于对象引用类型==比较的是对象的内存地址。对于基本数据类型==比较的是值。
  • 注意只有new Integer不一样点不管new的这个数是不是在-128到127这个范围内都会在堆中创建一个新的对象而不是使用IntegerCache缓存中的值
public class a {
    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 a =new Integer(3);是创一个对象在堆里 Integer b =3;是直接从IntegerCache缓存中拿-128到127这个范围内的Integer对象已经在这个IntegerCache缓存中创建好了的把b的引用指向IntegerCache缓存中的3。所以第一个判断为false。

第二个和第三个都是因为会拆箱所以就是int之间的值比较自然是true

package anomyous;

public class Test {


    public static void main(String[] args) {
        Integer i1=new Integer(99);
        Integer i2=Integer.valueOf(99);//手动装箱
        Integer i3=99;//自动装箱 隐式调用Integer.valueOf()

        System.out.println(i1==i2);//false
        System.out.println(i2==i3);//true  
        System.out.println(i1==i3);//false

    }
}
public static void main(String[] args){
    Integer a1 =128; 
    Integer b1 =128; 
    System.out.println(a1 == b1);// false 
    Integer a2=127;   
    Integer b2 =127;    
    System.out.println(a2 == b2);// true
} 

骚戴理解如果整型字面量的值在-128到127之间那么自动装箱时不会new新的Integer对象而是直接引用常量池中的Integer对象超过范围 a1==b1的结果是false

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Java

“Java基础知识面试题” 的相关文章