java基础
发布日期:2021-10-12 21:31:51 浏览次数:2 分类:技术文章

本文共 6923 字,大约阅读时间需要 23 分钟。

看了一位大佬的总结:

 

面向对象的特性:封装,继承,多态

 

多态是怎么实现的?

    父类或接口定义的引用变量可以指向子类或者具体实现类的实例对象,由于程序调用方法是在运行期才动态绑定的,所以引用变量所指向的具体实例对象在运行期才确定

 

重载(overload)和覆盖/重写(override)的区别?

重载是一个类中多态的一种表现,指的是一个类中定义了多个同名的方法,他们要么参数类型不一样,要么参数个数不一样,要么顺序不一样

注意:1)重载是通过不同方法的参数来区分的

          2)不能通过方法的访问权限,返回值类型或抛出的异常类型来重载

         3)对于继承,如果父类的方法是私有的,就不能在子类中进行重载,如果子类也定义一个同名的方法,那么是一个新的方法

 

覆盖是指派生类函数覆盖基类函数的方法并对其进行重写,达到不同的效果

注意:

1)派生类的覆盖方法必须要和基类中覆盖的方法有相同的参数和函数

2)派生类的覆盖方法的返回值必须与基类中覆盖方法的返回值相同

3)抛出异常必须与基类抛出异常一致

4)基类中的覆盖方法不能为private,否则子类中的就是一个新的方法而不是覆盖

 

区别:1)覆盖是父类和子类之间的关系,重载是同一个类之间

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

 

 

什么是jvm?什么是字节码?

1.jvm:是运行java字节码的虚拟机,jvm针对不同的系统有不同是实现

2.字节码:在java中,jvm可以理解的代码就是字节码(.class文件),它不面对特定的处理器,只面向虚拟机。java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留以了解释性语言可移植的特点

 

 

String为什么是不可变的?

  不可变是指一个对象在创建完成后它的状态不能改变,那么这个对象就是不可变的,String是被定义为final的类,所以不可继承,而String内部实现是一个char数组,这个数组是private的,所以外部也不能访问,因此,一旦赋值,不可更改

 

final的用法?

1.修饰一个类时,该类不能被继承

2.修饰一个方法后,该方法不能被重写

3.修饰一个变量后,该变量无法改变,假如是修改一个引用,则不能指向另外一个对象,但对象本身可变

 

如何通过反射创建一个对象?

   1.通过类对象调用newInstance()方法

  2.通过类对象的getConstructor()

 

java内存模型?

  java线程之间的通信由java内存模型(JMM)控制,JMM决定一个线程对共享变量的修改何时对另一个线程可见,JMM定义了线程与主内存的抽象关系:线程之间的变量存储在主内存中,每个线程都有一个私有的本地内存保存着共享变量的副本

 

为什么要有包装类,装箱和拆箱是什么,底层原理是什么?

   由于我们不能直接向集合中放入原始类型的值,因为集合只接收对象,所以假如我们要向集合中放入基本数据结构的话就得把基本类型包装成一个对象,这个时候就用到了包装类。但是这样就使得代码不是那么简洁精炼了,为了使代码更加简洁,引入了自动装箱和拆箱机制

    自动装箱就是java会自动把原始类型值转换为对应的包装类,将包装类转换为基本类型就是自动拆箱

    自动装箱时编译器通过调用valueOf将原始类型转换成对象,自动拆箱时通过调用类似intValue,doubleValue方法将对象转换为基本类型

   假如我们有一个方法,是接收一个对象类型的参数的,这时候我们传递一个基本类型的值时就会被java自动包装成包装类。假如我们要从一个集合中获取一个值时,就会进行自动拆箱,比如将Integer转换成int

 

阻塞队列?

   阻塞队列也是一种队列,它是一种可以在多线程环境下使用,并且支持阻塞等待的队列

        特点:1.多线程下也可以安全的访问队列

                    2.支持生产和消费等待,多个线程之间互相配合,当队列为空的时候,消费者线程会阻塞直到等待队列不为空,当等待队列满了后,生产者线程会阻塞直到等待队列非满

     分类:ArrayBlockingQueue    LinkedBlockingQueue

           ArrayBlockingQueue:是一个有界阻塞队列,需要提供数组的大小。构造函数的参数为capacity和fair分别代表数组的容量和阻塞队列的选择策略,选择策略用于构造线程同步的锁,它是使用的ReentrantLock在做同步的,有两个condition变量notFull和notEmpty,notEmpty.signal()方法用来唤醒等待在notEmpty上的线程。notEmpty这个条件变量用于表示队列是否有数据,这时候可能有这个情况,当在插入数据之前,可能会有线程已经尝试获取数据了,那么就可以让这个线程等待在这个条件变量上面,当插入数据之后再通过signal唤醒

      方法: 1.put:是一个阻塞方法,在操作不能够立即得到执行的时候会阻塞等待,就是当发现队列使用的数组没有可用的容量时就会等待在条件变量上,这个条件变量需要在有空闲的时候唤醒等待在它上面的线程

                2.offer:该方法在插入操作不能立即执行的时候会返回false,否则返回true代表插入成功,在多线程的环境下需要通过锁来实现同步

              3.take:会在元素获取失败的时候阻塞直到有线程唤醒它

             4.poll:从队列中获取数据,而offer是插入数据(可以设置超时时间)

    LinkedBlockingQueue:使用链表作为阻塞队列的数据结构,它的底层是一个双向链表,在读和写上都加上了锁

           

    

protected和default的区别?

      protected:包内所有类可见,包外有继承关系的子类可见

      default:包内所有类可见,包外有继承关系的子类不可见

 

值传递和引用传递,String和char作为参数,结果会改变吗?

   值传递:方法调用时,实际参数把它的值传递给对应的形式参数,方法执行中的参数值不改变实际参数值的值

  引用传递:也称作地址传递,方法调用时,传入的是参数的地址,方法执行中会改变实际参数

   string不会改变,char会改变,string是final char[]的,所以作为形参不会改变值

 

+,String,StringBuilder,StringBuffer?

     String是一个final类,是不能够被继承的,它的成员方法都是final的,所以方法也不能被覆盖,String的底层是通过一个final的char数组来进行保存字符串的,String的+是创建一个新的String对象,它的substring()和concat方法都不是在原来的字符串进行操作的,而是创建一个新的String对象,意思就是对String对象的任何改变都不会影响到原来的对象

    String str = "hello world"可能创建一个对象,也看不创建对象,假如常量池中不存在该对象,那么就创建,假如存在,就会直接利用原来的值,也就是str指向那个常量的内存地址,这样无论你创建多少个“hello world”,都只有一个内存地址被分配,而假如通过new String(“hello world”)来创建一个新的string时,就不是这种情况了,这个时候可能创建一个对象也可能创建两个,首先,堆里面的对象是可以重复的,那么堆首先会创建一个,然后加入常量池没有,就会再创建一个,所以每次new都是一个新的对象

        三者的区别:string用+时是创建一个新的对象,然后让原来的string指向新的对象的内存地址。而StringBuilder是变量,只生成了一个对象,new操作只有一次,它的+不会新创建对象,该操作是在原有对象上进行的,所以,当进行大量的修改字符串的操作时,选StringBuilder的好处大得多,而StringBuffer其实和StringBuilder几乎一样,除了StringBuffer是线程安全的

       三者的适用场景:假如是在循环体外的拼接,用string和StringBuilder一样。假如在循环体内,用StringBuilder,毫无疑问,当然,多线程下用StringBuffer来代替StringBuilder

 

什么是线程安全?

       如果代码所在的进程中有多个线程同时运行,而这些线程可能会同时运行这段代码,假如每次运行的结果都和单线程下运行的结果一样,那么就是线程安全的

 

Object的方法有哪些?

    hashcode(),equals(),clone(),toString(),finalize()

 

什么是深拷贝与浅拷贝?

   浅拷贝:被复制的对象的所有变量都与原对象有相同的值,索引的引用对象任然指向原来的对象,也就是说浅拷贝不复制引用对象

  深拷贝:除了被复制的对象的所有变量都有原来的对象的值之外,还把引用对象也指向了被复制的对象

 

继承和组合的区别?

    组合可以显示地获得被包含类的对象,而继承是隐式地获得父类的对象的

   组合是在运行时期确定的,继承是在编译时期决定的

   组合是一种松耦合,继承是紧耦合

   在使用继承关系时,可以实现类型的回溯, 即父类变量引用子类变量,通过此实现多态,而组合不行

 

byte a=127,byte b = 127, a = a+b和a += b的区别?

   +=操作会将操作完成后的结果隐式转换为与左侧相同的类型,而 = 不会。由于在java中,byte,short,int在进行加减操作时都会被隐式转换为int类型,所以如果是a = a+b的话,那么就会报错,因为左侧是byte类型,而右侧是int类型。 a += b 就不会,这个操作会将已经转换为int类型的结果再隐式转换为byte类型

 

谈谈对oop的理解?

   oop就是面向对象编程,特征是:封装,继承,多态,抽象

   封装:将对象信息状态通过访问权限修饰符隐藏在对象内部,不允许外部程序直接访问,但是可以通过get或set方法来进行访问

   继承:子类继承了父类所有的成员方法和属性,并且拥有自己的特性,解决了代码重用的问题

   多态:多态存在的三个条件就是继承,重写,父类引用指向子类对象,实现方法有接口实现,重写父类方法,同一个类中进行重载

  抽象:如果一个类中含有抽象方法,则称为抽象类

 

 

final和finally的区别?

 

final与finally的却别

  final可修饰:

     1.修饰变量

      2.修饰方法

      3.修饰类

     首先来看第一种情况,如果final修饰的是一个基本类型,就表示这个变量被赋予的值是不可变的,它是个常量,  final在修饰方法的传入参数时,则传入参数时不可修改的;

如果final修饰的是一个对象,就表示这个变量被赋予的引用是不可变的,这里这个不可改变的只是这个变量所保存的引用,并不是这个引用所指向的对象。

      在final修饰方法时,子类不能重写该方法。

在final修饰类是,被修饰的类是不可以被继承的

      被fianl修饰的变量必须被初始化,初始化的方式有:

         1.在定义的时候初始化

         2.在构造器中初始化,但静态的final是不可以的

         3.final变量可以在构造块中初始化,如果是静态的就不能再非静态的构造块中初始化

 

      finally就比较简单了,它只能用在try/catch语句中,并且附带着一个语句块,表示这段语句最终总是被执行。

即使在try语句中有return等结束方法或循环的代码被执行后,finally中的内容仍然会被执行

 

 

mybatis中#{}和${}的区别?

   在mybatis的映射配置文件中,动态传递参数有两种方式:

       1)#{}占位符:是参数占位符,即sql预编译。过程是动态解析——预编译——执行,变量替换在dbms中,变量替换后会自动加上单引号,能防止sql注入

       2)${}拼接符:是字符串替换,即sql拼接。过程是动态解析——编译——执行,变量替换在dbms外,变量替换后不会自动加上单引号,不能防止sql注入

 

mybatis的缓存实现,如果数据库进行了修改缓存怎么办?

   sqlsession一级缓存和二级缓存

   如果数据库进行了修改操作那么一级缓存会失效,而由于二级缓存的作用域是namespace,如果有连表查询,而这个连表查询是写在namespace里面的,那么就会导致脏数据产生

 

mybatis与hibernate的区别?

    hibernate对数据库结构提供了较为完整的封装,mybatis主要着力点在于java对象与sql之间的映射关系

    hibernate的数据库移植性远大于mybatis,hibernate通过映射结构与hql语言降低了对象与数据库的耦合性,而mybatis需要手写sql所以移植性没那么好

    hibernate拥有完整的日志系统,包括sql记录,关系异常,优化警告,缓存提示等等,而mybatis除了基本记录功能外就比较薄弱了

   在对sql的优化上,mybatis比hibernate方便。因为mybatis的sql写在xml里,而hibernate的很多都是自动生成的

   缓存机制上:hibernate的二级缓存配置在sessionFactory生成的配置文件中,可以详细配置,并且出现脏数据会报错并提示。而mybatis在使用二级缓存时需要特别小心,如果不能完全确定数据更新操作的范围,可能会导致脏数据的出现,而且不会提示

  重点就是缓存机制的不同

 

 

java反射?

   反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性

   优点:可以在运行期间判断,动态加载类,提高代码灵活度

   缺点:反射比直接的java代码慢很多,而且反射可以动态地改变类的属性所以增加了类的安全隐患

 

为什么要反射而不是直接new?

   反射可以实现高内聚,低耦合。它不用知道类名,可以直接实例化类,不用硬编码

 

transient?

   transient关键字的作用就是: 在不需要序列化的属性前添加该关键字就能够在序列化对象的时候不去序列化这个对象

   java的序列化提供了一种存储对象状态的机制,序列化就是把对象的状态存储到硬盘上,等到需要的时候再把它读出来使用。有时候我们只希望把指定属性的生命周期限制在调用者的内存中而不是写到磁盘里持久化,这个时候就可以加上transient来禁止序列化也就是禁止写到磁盘中。(比如银行卡密码等就不希望被序列化)

   静态变量是不会被序列化的,即使没有被transient修饰也不会被序列化

 

jdk8的stream和lambda表达式?

  lambda使得java能够进行简单的函数式编程,它是一个匿名函数。以前认为面向对象编程就是纯粹的面向对象,这个时候如果想写一个方法,就必须把方法放到一个类里,然后创建对象再调用方法,但是这样做太过于冗余,而lambda就是为了解决这个问题。要使用lambda表达式,方法声明时必须包含一个函数式接口,它只能出现在目标类型为函数接口的上下文中

  stream配合上lambda表达式和链式结构对集合能够进行很多有用的操作。可以对元素集合的并行和顺序操作,能够使数据操作更容易和更快,通过它可以对collection的元素进行过滤,映射,排序,去重等操作

 

 

java中为什么要有泛型?

  泛型的本质是参数化类型。它可以消除代码中的强制类型转换,同时获得一个附加的类型检查层,该检查层可以防止有人将错误类型的键或值保存在集合中

   好处:1.类型安全:java引入泛型的主要目标是提高类型安全,知道了使用泛型定义的变量的类型限制,编译器就可以在高层次上验证类型假设

             2.消除强制类型转换:提高了代码的可读性,减少了出错机会

             3.有潜在的性能收益:泛型的实现方式支持泛型不需要jvm或类文件的更改,所有工作都在编译器中完成,为未来优化jvm带来了可能

 

 

java的泛型擦除?

   java中的泛型擦除是指编译后的字节码文件中类型信息被擦除,变为原生类型。也就是说ArrayList<Integer>和ArrayList<String>在编译时是同一个类。在泛型擦除时如果类型参数没有指定上限就会被转成Object类型,指定了上限的话就会被替换为类型上限。但是java中泛型擦出并不是每个地方都会进行擦除的,比如在运行期间,假如方法中出现带泛型的匿名内部类,那么这个泛型就会被保留下来

 

mybatis和jdbc的区别?

  jdbc:是java提供的一个操作数据库的api

  mybatis:是一个支持普通sql查询,存储过程和高级映射的持久层框架,它几乎消除了所有的jdbc代码和参数的手工设置以及对结果集的封装。mybatis可以使用简单的xml或者注解用于配置映射,将接口和java的pojo映射成数据库中的记录

  mybatis就是的jdbc的封装

     优点:1.优化了获取和释放,可以在DataSource里配置数据库连接池

               2.对sql进行统一管理,放在配置文件中以后就可以直接修改配置文件而不用重新编译

              3.生成动态的sql语句

               4.能够对结果集进行映射

转载地址:https://blog.csdn.net/qq_40058686/article/details/104436161 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:操作系统
下一篇:String,StringBuilder,StringBuffer的区别

发表评论

最新留言

很好
[***.229.124.182]2024年04月02日 22时05分17秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章