Java程序员面试核心知识--Java基础知识(一)

目录

一、Java程序初始化顺序

二、Java的Clone方法作用

三、 OverLoad(重载)与Override(重写)区别

四、abstract class(抽象类)与interface(接口)的异同

五、String、StringBuffer、StringBuilder区别

六、“==”、equals和hashCode的区别

七、字符串创建与存储的机制


一、Java程序初始化顺序

结论:父类静态变量->父类静态代码块->子类静态变量->子类静态代码块->父类非静态变量->父类非静态代码块->父类构造方法->子类非静态变量->子类非静态代码块->子类构造方法

代码例题,请写出下面程序的运行结果

package com.alphamilk.Example101;class Father{static String staticString = "a";static {System.out.println(staticString);System.out.println("b");}public  String NonString = "c";{System.out.println(NonString);System.out.println("d");}
}
class Son extends Father{static String SonstaticString = "e";static {System.out.println(SonstaticString);System.out.println("f");}public  String SonNonString = "g";{System.out.println(SonNonString);System.out.println("h");}
}
public class javaDemo {public static void main(String[] args) {Son son = new Son();}
}


二、Java的Clone方法作用

        由于Java在处理基本数据类型(如int、double、char等)时候,都是采用按值传递,除此之外的数据类型都是按照引用传递方式执行

这就会导致数据类型的备份出现问题,基本数据类型可以通过值传递快速备份,而对象引用会传递对象的地址,这样会导致对备份的操作影响到原本的数据。如下案例:

// 其它类型数据
class Person{public String name;public int age;Person(String name ,int age){this.name = name;this.age = age;}@Overridepublic String toString() {return "name:"+name+"  age:"+age;}
}
public class JavaDemo {public static void main(String[] args) {
//        基本数据类型与备份int a  = 10;int b;b = a;
//        对备份操作b = 100;System.out.println(a);System.out.println(b);
//        其它类型与"="引用Person person = new Person("小红",100);Person person1 = person;
//        对备份进行操作person1.name = "小黄";System.out.println(person);System.out.println(person1);}
}

可以看到由于对象类型的“=”是引用传递,传递的是对象的地址,所以对备份的操作实际上就是对本体的操作.

所以想要使用备份就需要Object类中的Clone方法。而Object类是所有类的父类,所以可以如下实现

1.实现Cloneable接口 2.覆写Clone方法 3.调用方法

//1.实现 Cloneable 接口
class Person implements  Cloneable{public String name;public int age;Person(String name ,int age){this.name = name;this.age = age;}@Overridepublic String toString() {return "name:"+name+"  age:"+age;}
//   2. 实现克隆方法public Person Clone(){Person per  = null;try {
//            强制转型per = (Person)super.clone();}catch (CloneNotSupportedException e){e.printStackTrace();}return per;}
}
public class JavaDemo {public static void main(String[] args) {Person person = new Person("小红",100);
//        3.调用Clone()方法Person person1 = person.Clone();//        对备份对象进行操作person1.name = "小黄";person1.age=12;
//        输出对象System.out.println(person);System.out.println(person1);}
}

注意:上述代码克隆是在对象中仅存在基本数据类型下,这样的拷贝被称为浅拷贝。而一旦对象中还存在其它对象,则需要用深拷贝实现拷贝内容.

Clone分为浅拷贝和深拷贝

浅拷贝:仅仅拷贝对象中只存在基本数据类型

深拷贝:可以拷贝对象中包含的对象

深拷贝如下案例:

class Student implements Cloneable{
//    基本数据类型int id;
//    其它引用对象类型Dateprivate Date birthDay = new Date();public Date getBirthDay() {return birthDay;}
//    更改BirthDay方法public void  ChangedBirthDay(){long intervalTime = 3600*30;long time  =  birthDay.getTime();birthDay.setTime(time+intervalTime);}//    覆写克隆方法public  Student Clone(){Student student = null;
//        浅层拷贝try {student = (Student) super.clone();} catch (CloneNotSupportedException e) {throw new RuntimeException(e);}
//        深层拷贝student.birthDay = (Date) this.getBirthDay().clone();return student;}@Overridepublic String toString() {
//        输出对象return "Student's id:"+id+" birthday is " +birthDay;}
}
public class JavaDemo2 {public static void main(String[] args) {Student student1 = new Student();student1.id = 10;Student student2 = student1.Clone();student2.id = 11;student2.ChangedBirthDay();System.out.println(student1);System.out.println(student2);}

深层拷贝相比与浅层拷贝,需要额外对引用对象中其它对象进行拷贝。


三、 OverLoad(重载)与Override(重写)区别

overload 与 override 都是Java多态性的不同表现,其中overload是类中多态性的显现,

Overload重载

重载指的是同一个类中有多个方法名称相同,但是参数不同的方法,因此可以在编译时候就确定具体调用哪个方法。它是一种编译时候的多态,重载可以看作一个类的多态。

Override重写

子类可以重写父类的方法,因此同样的方法在父类和子类中有着不同的表现形式,在Java语言中,基类的引用变量不仅可以指向其实现类的实例对象,也可以指向其子类的实例对象。同样,接口的引用变量中可以指向其实现类的实例对象。

案例代码:

class Person{String race;
//    父类方法public String printfInfo(String race){return "种族"+race;}
}
class Student extends  Person{int id;String name;
//    实现构造方法重载(OverLoad)//    1.无参构造方法Student(){}
//    2.单参构造方法Student(String name){this.name = name;}
//    3,多参构造Student(String name ,int id){this.name = name;this.id = id;}
//    覆写方法/** 注意,覆写时候参数也需要一致*/@Overridepublic String printfInfo(String race) {return "种族"+race+" , 名称:"+name +" id"+id;}
}
public class JavaDemo {public static void main(String[] args) {Student student = new Student("Alpha",01);System.out.println(student.printfInfo("人类"));}
}

区别:

1.覆写是子类和父类之间的关系,是垂直关系,而重载是同一个类中方法之间的关系,是水平关系。

2.覆写只能由一个方法或者一对方法产生关系,方法的重载是多个方法之间的关系。

3.覆写要求参数列表相同,而重载要求参数列表不同

4.覆写关系中,调用方法体是根据对象的类型(对象对应存储空间类型)来决定的,而重载关系是根据调用时的实参列表和形参列表来选择方法体的。


四、abstract class(抽象类)与interface(接口)的异同

抽象类

        如果一个类中包含抽象方法,那么这个类就是抽象类。在Java中可以通过某些类或者是类中的方法声明为abstract(abstract只能用来修饰类或者方法,不能用来修饰属性)来表示一个类是抽象类。只要包含一个抽象方法的类就必须声明为抽象类,抽象类可以仅仅申明方法的存在而不去实现它,被声明为抽象的方法不能包含方法体。在实现的时候,必须包含相同的或者更低的访问级别(public -> protectd ->private ).抽象类在使用的过程中不能被实例化,但是可以创建一个对象使其指向具体子类的一个示例,抽象类的子类为父类中所有的抽象方法提供具体的实现。否则子类也将是抽象类。

接口

        接口就是一个方法的集合,在Java中,接口是通过关键字interface来实现的,在Java8之前,接口中既可以定义方法也可以定义变量,其中变量必须是public、static、final的。而且方法必须是public abstract 的。由于这些修饰符都是默认的。

        在Java8之后,可以通过关键字default给接口中的方法添加默认实现,此外接口中还可以定义静态方法。

案例代码:

interface category{
//    输出物种信息public void printInfo();
}
interface Breath{
//    生物进行呼吸public void Breath();
}
abstract class behavior{
//    定义抽象方法public abstract void shout();
//    定义普通方法public void jump(){System.out.println("进行跳跃");}
}
//只能继承一个抽象类
//但是能继承多个接口
class Person extends behavior implements Breath,category{//    覆写抽象类中的抽象方法@Overridepublic void shout() {System.out.println("人类进行嚎叫");}//    实现接口category中的方法@Overridepublic void printInfo() {System.out.println("种族,人类");}
//    实现接口Breath方法@Overridepublic void Breath() {System.out.println("呼吸类型:有氧呼吸与无氧呼吸");}
}
public class JavaDemo  {public static void main(String[] args) {Person person = new Person();
//        执行覆写的方法person.printInfo();person.Breath();person.shout();}
}

不同点

抽象类

  1. 抽象类只能被继承(通过extend),并且一个类只能继承一个抽象类
  2. 抽象类强调所属关系,其设计理念为is-a关系。
  3. 抽象类更倾向于充当公共类的角色,不适与日后重新对立面的代码进行修改。
  4. 除了抽象方法以外,抽象类中还可以包含具体数据和具体方法(可以由方法的实现)。
  5. 抽象类不能被实例化,其子类如果实现了继承中抽象类中的抽象方法后可以,实例化子类,否则子类也是抽象类不能被实例化。

接口

  1. 接口需要通过实现(implements),一个类可以实现多个接口
  2. 接口用于实现比较常用的功能,以便于日后的维护和方法的增删。
  3. 接口不是类,而是一种对类的一组需求描述,这些类需要遵循接口描述的统一格式进行定义。
  4. 接口中所有的方法都是public的,因此,再实现接口的类中必须把方法声明为public,因为默认类中访问属性是包可见的。而不涉及public,这就相当于在子类中降低了方法的可见性,会导致编辑错误。

五、String、StringBuffer、StringBuilder区别

在Java中,可以操作字符串的有几大类,包括String、StringBuffer、StringBuilder。

        String是不可变类,也就意味着一旦String对象一旦创建,其值无法被改变,而StringBuffer是可以改变的。所以当一个字符串需要经常修改的时候,最好用StringBuffer来实现。因为不可变类,其值在修改的时候,其实原来的数据并没有被删除,而是堆上创建了一个新的不可变类String的对象,并且栈上对象指向了新的对象,所以如果字符串大量修改的话,就会产生大量的浪费资源的无用对象。最后被垃圾回收器回收。虽然这在小的项目里影响微乎其微,但是在大项目中是会有非常大的效率问题。

        StringBuilder也是可以被修改的字符串,与StringBuffer相似,都是字符串的缓冲区,但是StringBuilder是线程不安全的。如果只是在单线程的情况下进行操作,那么StringBuilder的效率是最快的。而多线程使用的时候StringBuffer使用是最好的。因为StringBuffer在有需要的时候可以进行方法的同步。

        String与StringBuffer还有一点不同是在于初始化对象上,String有两种初始化方式第一种是直接赋值,第二种是通过构造函数赋值,而StringBuffer只能通过构造函数创建对象。

使用的案例代码:

public class JavaDemo {public static void main(String[] args) {
//        创建有序map,查看效率排名Map<Long,String> map = new TreeMap<>();//        String的两种初始化对象方式String a ="String A";String b =new String("String B");//        StringBuffer唯一初始化对象方式StringBuffer buffer = new StringBuffer("String C");
//        StringBuilder初始化方式StringBuilder builder = new StringBuilder("String D");//        单线程下分别测试效率
//        String字符串long start = System.currentTimeMillis();for (int i = 0;i<10000;i++){a = a + "Add String";}long end = System.currentTimeMillis();long internal1 = end - start;map.put(internal1,"String类型运行时间");//       StringBufferlong start2 = System.currentTimeMillis();for (int i=0;i<10000;i++){
//            调用StringBuffer内方法append,即添加字符串buffer = buffer.append("Add String");}long end2 = System.currentTimeMillis();long internal2 = end2 - start2;map.put(internal2,"StringBuffer类型运行时间");//        StringBuilderlong start3 = System.currentTimeMillis();for (int i=0;i<10000;i++){builder = builder.append("Add String");}long end3 = System.currentTimeMillis();long internal3 = end3 - start3;map.put(internal3,"StringBuilder类型运行时间");System.out.println(map);}
}

可以多次运行代码或者提高循环次数,查看平均值

单线程保证线程安全下 效率速度: StringBuilder>StringBuffer>String


六、“==”、equals和hashCode的区别

        “==”运算符在基本数据类型中是用于判断两个变量的值是否相同,但是如果两个变量是引用数据类型,那么“==”就是两个变量对应的引用地址的比较。

        equals是Object类中提供的方法,由于所有类都继承了Object主类,所以所有对象都是可以调用equals方法。相比于“==”运算符,equals方法的特殊之处就是在于它是可以被覆盖的,也就意味着它并不是比较引用的地址,而是比较对象中的属性。

        hashCode方法是从Object类中继承过来的,他也是用来鉴定两个对象是否相等。Object类中的hashCode()方法返回对象在内存中的地址并转换成一个int值,所以如果没有重写hashCode()方法,任何对象的hashCode方法都是不相等的。也即是哈希值唯一的

请你写出下面程序的执行结果:

class Person{String name;Person(String name){this.name = name;}
}
public class JavaDemo {public static void main(String[] args) {String a = new String("content");String b = new String("content");String c = "content";String d = "content";System.out.println("1:"+ (a==b));System.out.println("2:"+a.equals(b));System.out.println("3:"+(c == b));System.out.println("4:"+ (c == a));System.out.println("5:"+ (d == c));int base1 = 1 ,base2 = 1;Integer integer = 1;Integer integer1 = 1;System.out.println("6:"+ (base1==base2));System.out.println("7:"+ integer1.equals(integer));Person person = new Person("张三");Person person1 = new Person("张三");System.out.println("8:"+(integer.hashCode() == integer1.hashCode()));System.out.println("9:"+(c.hashCode() == a.hashCode()));System.out.println("10:"+(person.hashCode() == person1.hashCode()));}
}

注意:

  9:true 解释 --String 类是一个特殊的类,它有一个特殊的属性,即字符串常量池(String Pool)。当我们使用字面量创建字符串时,例如 String c = "content",Java会首先检查字符串常量池中是否已经存在相同内容的字符串,如果存在,则直接返回该字符串的引用;如果不存在,则在常量池中创建一个新的字符串对象,并返回其引用。

因此,在这段代码中,虽然 ab 是通过 new 关键字创建的两个不同的字符串对象,但它们的内容相同。而 cd 则是通过字面量创建的字符串,它们的内容也相同。由于字符串常量池的存在,cd 实际上是指向同一个字符串对象的引用,因此它们的地址是相同的。

   10:true解释 --对于 Integer 类型的对象,Java在内部维护了一个缓存池,用于缓存-128 ~ 127之间的整数对象。当我们通过 Integer.valueOf() 方法创建一个整数对象时,如果该整数在缓存池中已经存在,则直接返回该整数对象的引用;否则,在堆内存中创建一个新的整数对象,并将其加入到缓存池中,然后返回其引用。


七、字符串创建与存储的机制

在Java语言中,对String对象提供给了专门的存储机制。

public class StringPool {public static void main(String[] args) {//           第一种存储
//        将abc放到常量区abc,在编译时候产生String s = "abc";
//        将ab与c转为字符串常量“abc”并存在常量区String s1 = "ab"+"c";//            第二种存储
//        在堆中创建新的对象,堆中对象指向常量区String s2 = new String("abc");String s3 = new String("abc");//        分别输出四个常量地址System.out.println("s地址"+s.hashCode());System.out.println("s1地址"+s1.hashCode());System.out.println("s2地址"+s2.hashCode());System.out.println("s3地址"+s3.hashCode());}
}

第一种存储String s = "abc"

        在当创建一个字符常量的时候,会首先在字符串常量池中进行查找是否已经有相同的字符串常量。具体原理就是通过调用方法equals方法进行一 一匹配 ,如果不存在则需要先进行创建一个对象,然后将其加入字符串池中,再将它引用返回。而如果存在的话,直接获取对其的引用。而能够设置一个常量池的原因在于String类是一个不可变类,无法对其进行修改,日常对其进行修改本质是创建了一个新的字符串。

第二种存储String s1 = new String("abc");

        有区别第一种,第二种存储需要对其操作进行拆分,其多了一个new操作,这个操作是在堆上创建一个对象,并将值存放在堆中。是独立进行存储的,与常量池无关。

从分析中可以看出,在创建字符串对象时候,会根据不同的情况来确定字符串是存储在堆中还是常量池中。而intern方法就是用于将字符串存入常量池中。 所以通过intern方法可以将new关键字创建堆内的字符串对象存入字符串常量池中。

但是要注意的是intern方法随着JDK的更新,方法也随着更新。以下是其JDK1.6以下和JDK1.7以后的一些区别。

  • 在 JDK 1.6 及之前的版本中,调用 intern() 方法会将字符串复制一份到字符串常量池,并返回字符串常量池中的引用。如果字符串常量池中已经存在相同值的字符串,intern() 方法会返回常量池中的引用;否则,会在常量池中创建一个新的实例并返回该引用。
  • 而在 JDK 1.7 及之后的版本中,调用 intern() 方法会检查字符串常量池中是否存在相同值的字符串。如果存在,则返回常量池中的引用;如果不存在,则将字符串的引用直接存储到字符串常量池中,并返回该引用。换句话说,对于 JDK 1.7 及之后的版本,如果原始字符串已经在常量池中存在,调用 intern() 方法不会创建新的实例,而是直接返回常量池中的引用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/156669.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(免费领源码)springboot#MYSQL钓鱼交友平台小程序33506-计算机毕业设计项目选题推荐

目 录 摘要 1 绪论 1.1课题目的与意义 1.2国内外研究现状 1.3论文结构与章节安排 1.4小程序框架以及目录结构介绍 2 Springboot钓鱼交友平台小程序系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 操作可行性分析 2.2 系统流程分析 2.2…

Vue项目搭建及使用vue-cli创建项目、创建登录页面、与后台进行交互,以及安装和使用axios、qs和vue-axios

目录 1. 搭建项目 1.1 使用vue-cli创建项目 1.2 通过npm安装element-ui 1.3 导入组件 2 创建登录页面 2.1 创建登录组件 2.2 引入css&#xff08;css.txt&#xff09; 2.3 配置路由 2.5 运行效果 3. 后台交互 3.1 引入axios 3.2 axios/qs/vue-axios安装与使用 3.2…

React使用富文本CKEditor 5,上传图片并可设置大小

上传图片 基础使用&#xff08;标题、粗体、斜体、超链接、缩进段落、有序无序、上传图片&#xff09; 官网查看&#xff1a;https://ckeditor.com/docs/ckeditor5/latest/installation/integrations/react.html 安装依赖 npm install --save ckeditor/ckeditor5-react cked…

Linux 远程桌面软件

为您的 IT 管理员配备最好的 Linux 远程桌面软件至关重要。原因如下&#xff1f;Linux 是一个开源和免费的操作系统&#xff0c;它提供了一个非常灵活和可定制的软件内核。由于其开源性质&#xff0c;Linux 被认为是市场上最安全的操作系统之一&#xff0c;它拥有一个全球用户社…

Linux C语言进阶-D7~D8指针与数组

一维数组 一维数组的数组名&#xff1a;就是一维数组的指针&#xff08;起始地址&#xff09; 如果int *px x;则&#xff1a; x[i]、*(pxi)、*(xi)、px[i]具有完全相同的功能&#xff1a;访问数组第i1个数组元素 注意&#xff1a;1、指针变量和数组在访问数组元素时&#x…

C++——list

目录 list介绍 list的函数接口 构造函数 push_front和pop_front push_back和pop_back insert erase 迭代器 front和back size resize empty clear list::sort unique reverse 迭代器的实现 list介绍 list是一种可以在常数范围内在任意位置进行插入和删除的序列…

《HelloGitHub》第 91 期

兴趣是最好的老师&#xff0c;HelloGitHub 让你对编程感兴趣&#xff01; 简介 HelloGitHub 分享 GitHub 上有趣、入门级的开源项目。 github.com/521xueweihan/HelloGitHub 这里有实战项目、入门教程、黑科技、开源书籍、大厂开源项目等&#xff0c;涵盖多种编程语言 Python、…

ESM蛋白质语言模型系列

模型总览 第一篇《Biological structure and function emerge from scaling unsupervised learning to 250 million protein sequences 》ESM-1b 第二篇《MSA Transformer》在ESM-1b的基础上作出改进&#xff0c;将模型的输入从单一蛋白质序列改为MSA矩阵&#xff0c;并在Tran…

Android NDK开发详解之Application.mk探秘

Android NDK开发详解之Application.mk探秘 概览变量APP_ASFLAGSAPP_ASMFLAGSAPP_BUILD_SCRIPTAPP_CFLAGSAPP_CLANG_TIDYAPP_CLANG_TIDY_FLAGSAPP_CONLYFLAGSAPP_CPPFLAGSAPP_CXXFLAGSAPP_DEBUGAPP_LDFLAGSAPP_MANIFESTAPP_MODULESAPP_OPTIMAPP_PLATFORMAPP_PROJECT_PATHAPP_STL…

剑指JUC原理-8.Java内存模型

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码&#x1f525;如果感觉博主的文章还不错的话&#xff0c;请&#x1f44d;三连支持&…

SwiftUI 如何为文件和图片加入便捷的快速预览功能

功能需求 在 SwiftUI 开发的 App 中,我们有时需要以快速预览(QuickLook)的方式向用户展示文件内容,自己动手去处理上百种文件类型的预览是不现实的。 如上图所示:我们在 SwiftUI 中实现了任意文件的快速预览,这是如何完成的呢? 在本篇博文中您将学到如下内容: 功能需…

如何知道服务器的某个端口是否打开

注意&#xff1a;服务器的TCP端口&#xff0c;比如1886端口&#xff0c;出方向 和进方向 都打开才可以用 1、telnet 命令&#xff1a;telnet ip port&#xff0c;port即端口&#xff0c;我们一般最常见的命令就是telnet&#xff0c;但是telnet使用的是tcp协议&#xff0c;换句…