牛客网Java基础专项练习笔记
-
高优先级的线程不一定比低优先级的线程运行更快。
-
Java四类基本数据类型:
- 整型:byte,short,int,long
- 浮点型:float,double
- 逻辑型:boolean
- 字符型:char
-
构造方法不能被子类继承,所以不能用final修饰。构造方法用于创建一个新的对象,不能作为类的静态方法,所以不能用static修饰。Java不支持native或synchronized的构造方法。
-
以下代码运行结果:输出 “AB.B”
public class foo { public static void main(String sgf[]) { StringBuffer a=new StringBuffer("A"); StringBuffer b=new StringBuffer("B"); operate(a,b); System.out.println(a+"."+b); } static void operate(StringBuffer x,StringBuffer y) { x.append(y); y=x; } }
y本来指向的是b所指向的对象,但是一个“=”,y就指向了x所指向的目标即是a指向的对象,因此原来b所指向的目标并没有发生任何改变。
Java中只有按值传递,没有按引用传递。
-
Java数据库链接库JDBC使用了桥接模式(将抽象部分与它的实现部分分离,使它们都可以独立的变化。用来将抽象与实现解耦)
-
ArrayList的插入和删除操作会引发后续元素移动,效率低,但是随机访问效率高。
LinkedList的内存结构使用双向链表存储的,插入和删除效率高,但是随机访问效率低。
-
ClassLoader(类加载器)就是用来动态加载class文件到内存当中的。
JDK中提供了三个ClassLoader,根据层级从高到低为:
-
Bootstrap ClassLoader:主要加载JVM自身工作需要的类。
-
Extension ClassLoader:主要加载%JAVA_HOME%\lib\ext目录下的库类。
-
Application ClassLoader:主要加载Classpath指定的库类,一般情况下这是程序中的默认类加载器,也是ClassLoader.getSystemClassLoader()的返回值。(这里的Classpath默认指的是环境变量中配置的Classpath,但是可以在执行Java命令的时候使用-cp 参数来修改当前程序使用的Classpath)
JVM加载类的实现方式,我们称为 双亲委托模型:
如果一个类加载器收到了类加载的请求,他首先不会自己去尝试加载 这个类,而是把这个请求委托给自己的父加载器,每一层的类加载器都 是如此,因此所有的类加载请求最终都应该传送到顶层的Bootstrap ClassLoader中,只有当父加载器反馈自己无法完成加载请求时,子加载器才会尝试自己加载。
双亲委托模型的重要用途是为了解决类载入过程中的安全性问题。
假设有一个开发者自己编写了一个名为Java.lang.Object的类,想借此 欺骗JVM。现在他要使用自定义ClassLoader来加载自己编写的 java.lang.Object类。然而幸运的是,双亲委托模型不会让他成功。因为 JVM会优先在Bootstrap ClassLoader的路径下找到 java.lang.Object类,并载入它 。
-
-
被final关键字修饰的类不能被继承,所以final并不能修饰抽象类。
重载 的实现是编译器根据函数的不同的参数表,对同名函数的名称做修 饰,那么对于编译器而言,这些同名函数就成了不同的函数。但重写则 是子类方法对父类的方法的延申,即子类不仅继承了父类的方法,还向 父类的方法中添加了属于自己的内容,改变了父类方法原本的内容,而 final代表了一种不可变,这明显与重写形成了冲突。因此被final修饰的 类可以被重载但不能被重写。
当final用来修饰变量时,代表该变量不可被改变,一旦获得了初始值, 该final变量就不能被重新赋值。
Java Thread中:
- start方法: 用 start方法来启动线程,是真正实现了多线程, 通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run()方法。但要注意的是,此时无需等待run()方法执行完毕,即可继续执行下面的代码。所以run()方法并没有实现多线程。
- run方法 :run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码。
-
类的加载包括:加载,验证,准备,解析,初始化。
在类加载的时候会赋予初值的是类变量,而非对象成员。
-
public static void main(String args[]) { System.out.println(14^3); }
输出结果为13
原因 : ^ 表示异或,就是相同是0 不同是1
14是1110 3是 0011 所以14^3=1101,即13
-
Java中对于文本文件和二进制文件,都可以当作二进制文件进行操作。
-
Double 是Java定义的包装类,double 是Java定义的数据类型,不同的数据类型不能自动装箱拆箱 。
-
Java中的 **GC **是垃圾收集器(Garbage Collector)的简称。它是Java虚拟机的一部分,负责自动回收不再使用的对象占用的内存空间,以避免内存泄漏和提高内存利用率。当Java程序运行时,会不断创建新的对象,而GC会定期检查程序中的对象是否还在使用,如果某个对象已经没有被任何引用所指向,那么GC就会将其回收,以便为其他对象腾出内存空间。
-
在Java中重写方法时参数列表必须完全与被重写的方法相同。
-
Java一律采用Unicode编码方式,每个字符无论中文还是英文字符都占用2个字节。Java虚拟机中通常使用UTF-16(UTF-16是一种Unicode编码格式,它使用16位(2个字节)来表示Unicode字符集中的每个字符)的方式保存一个字符(char)。
-
Java类中的字段(对象)的默认访问权限是包访问权限。
-
JVM(Java虚拟机)主要组成部分及其功能:
- 类加载器(Class Loader):负责将Java类文件加载到JVM中,并将其转换为JVM可以识别的字节码。
- 字节码解释器(Interpreter):负责将字节码解释成机器码,并将其执行。
- 运行时数据区(Run-time Data Area):包括堆(Heap)、栈(Stack)、方法区(Method Area)等多个部分,用于存储Java对象、变量、常量等运行时数据。
- 垃圾收集器(Garbage Collector):负责自动回收不再使用的对象占用的内存空间,以避免内存泄漏和提高内存利用率。
- 线程管理(Thread Management):负责管理Java程序中的线程,包括线程的创建、销毁、调度等。
- 类定义(Class Definition):负责定义Java类的结构和属性,并将其保存在方法区中。
- 内存管理(Memory Management):负责管理JVM中的内存,包括堆内存、栈内存、方法区内存等。
-
方法的重写规则:
- 参数列表与被重写方法的参数列表必须完全相同。
- 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
- 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
- 父类的成员方法只能被它的子类重写。
- 声明为 final 的方法不能被重写。
- 声明为 static 的方法不能被重写,但是能够被再次声明。
- 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
- 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
- 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
- 构造方法不能被重写。
- 如果不能继承一个类,则不能重写该类的方法。
-
执行顺序:父类静态代码块 ->子类静态代码块 ->父类非静态代码块 -> 父类构造函数 -> 子类非静态代码块 -> 子类构造函数。
-
垃圾回收是完全自动的,不能被强制执行,不能确定具体的回收时间。
-
**<<**表示左移位
**>>**表示带符号右移位
**>>>**表示无符号右移
但是没有**<<<**运算符
-
Java多线程中start是开启线程,run是线程的执行体,run是线程执行的入口。
-
javac.exe是编译功能javaCompiler
java.exe是执行程序,用于执行编译好的.class文件
javadoc.exe用来制作java文档
jdb.exe是java的调试器
javaprof.exe是剖析工具
-
抽象类中的抽象方法(其前有abstract修饰)不能用private、static、synchronized、native访问修饰符修饰。
-
接口是一种特殊的抽象类,接口中的方法全部是抽象方法(但其前的abstract可以省略),所以抽象类中的抽象方法不能用的访问修饰符这里也不能用。而且protected访问修饰符也不能使用,因为接口可以让所有的类去实现(非继承),不只是其子类,但是要用public去修饰。接口可以去继承一个已有的接口。
-
假如某个JAVA进程的JVM参数配置如下:
-Xms1G -Xmx2G -Xmn500M -XX:MaxPermSize=64M-XX:+UseConcMarkSweepGC -XX:SurvivorRatio=3
请问eden区最终分配的大小是多少? 300M**解析:先分析一下里面各个参数的含义:
-Xms:1G , 就是说初始堆大小为1G
-Xmx:2G , 就是说最大堆大小为2G
-Xmn:500M ,就是说年轻代大小是500M(包括一个Eden和两个 Survivor)
-XX:MaxPermSize:64M , 就是说设置持久代最大值为64M
-XX:+UseConcMarkSweepGC , 就是说使用使用CMS内存收集算法
-XX:SurvivorRatio=3 , 就是说Eden区与Survivor区的大小比值为 3:1:1
题目中所问的Eden区的大小是指年轻代的大小,直接根据-Xmn: 500M和-XX:SurvivorRatio=3可以直接计算得出
500M(3/(3+1+1))
=500M(3/5)
=500M*0.6
=300M
所以Eden区域的大小为300M。 -
两个数值进行二元操作时,会有如下的转换操作: 如果两个操作数其中有一个是double类型,另一个操作就会转换为double类型。 否则,如果其中一个操作数是float类型,另一个将会转换为float类型。 否则,如果其中一个操作数是long类型,另一个会转换为long类型。 否则,两个操作数都转换为int类型。
-
true,false,null都不是Java关键字。
-
ThreadLocal类用于创建一个线程本地变量
在Thread中有一个成员变量ThreadLocals,该变量的类型是ThreadLocalMap,也就是一个Map,它的键是threadLocal,值就是变量的副本,ThreadLocal为每一个使用该变量的线程都提供了一个变量值的副本,每一个线程都可以独立地改变自己的副本,是线程隔离的。通过ThreadLocal的get()方法可以获取该线程变量的本地副本,在get方法之前要先set,否则就要重写initialValue()方法。
ThreadLocal不是用来解决对象共享访问问题的,而主要是提供了保持对象的方法和避免参数传递的方便的对象访问方式。一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。 -
HashMap允许将键/值为null。
-
json对象要求属性必须加双引号
使用 {} 则为json对象
json对象必须由一组有序的键值对组成
JSON语法可以表示以下三种类型的值:
- 简单值:使用与JavaScript 相同的语法,可以在JSON中表示字符串,数值,布尔值和null
- 对象:对象作为一种复杂数据类型,表示的是一组有序的键值对。而每组键值对中的值可以是简单值,也可以是复杂数据类型的值
- 数组:数组也是一种复杂数据类型,表示一组有序的值的列表,可以通过数值索引来访问其中的值。数组的值也可以是任意类型–简单值,对象或数组
-
static可以用来修饰内部类,但是不可以用来修饰外部类。
-
两个String类型变量都是常量池中的字符串,只有变量名不同是可以用双等号判断是否相等的,内存都是常量池中的字符串。
String底层源码的**equals()**方法处有判断这个参数是不是String类的实例,如果不是则不执行判断直接返回false。
-
只对写操作加锁,不对读操作加锁,虽然可以减少锁竞争,但是无法保证读取数据的一致性,容易导致数据不安全。
采用copyOnWrite的方式实现写操作,虽然可以保证写操作的线程安全,但是由于每次写操作都会复制整个数据结构,会带来较大的系统开销和延迟,不适用于读写平均的场景。
分区段加锁,可以将数据结构分为多个区段,每个区段使用单独的锁来实现线程安全,从而降低锁竞争,提高并发性能。
-
在Java中,可以将一个类定义在另一个类里面或者一个方法里边,这样的类称为内部类,广泛意义上的内部类一般包括四种:成员内部类,局部内部类,匿名内部类,静态内部类 。
- 成员内部类
- 该类像是外部类的一个成员,可以无条件的访问外部类的所有成员属性和成员方法(包括private成员和静态成员)
- 成员内部类拥有与外部类同名的成员变量时,会发生隐藏现象,即默认情况下访问的是成员内部类中的成员。如果要访问外部类中的成员,需要以下形式访问:【外部类.this.成员变量 或 外部类.this.成员方法】
- 在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问
- 成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象
- 内部类可以拥有private访问权限、protected访问权限、public访问权限及包访问权限。如果成员内部类用private修饰,则只能在外部类的内部访问;如果用public修饰,则任何地方都能访问;如果用protected修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。外部类只能被public和包访问两种权限修饰
- 局部内部类
- 局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内
- 局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的
- 匿名内部类
- 一般使用匿名内部类的方法来编写事件监听代码
- 匿名内部类是不能有访问修饰符和static修饰符的
- 匿名内部类是唯一一种没有构造器的类
- 匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写
- 内部静态类
- 静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似
- 不能使用外部类的非static成员变量或者方法
- 成员内部类
-
子类中的方法覆盖父类的方法以后,由于向上转型,父类调用的方法实际上是子类的。
-
在Java中,**
try-catch-finally
**语句块是一种用于异常处理的机制。try
块中的代码被尝试执行,如果有异常发生,则会跳转到相应的catch
块进行处理。无论是否发生异常,控制流都会到达finally
块。关于
finally
块中的return
语句,需要注意以下几点:- 在
try
或catch
块中的return
语句会被执行,并且在执行finally
块之前返回值已经被确定。 - 如果
finally
块中也有一个return
语句,那么它会覆盖try
或catch
块中的返回值。即使finally
块中的返回语句是在try
或catch
块之后执行的,也会覆盖先前的返回值。 - 如果
finally
块中没有return
语句,那么它会继续执行其他语句,并且不会改变try
或catch
块中的返回值。
- 在
-
Java线程的抢先机制(线程调度)有两种主要策略:协作式抢占 和 时间片轮转 。
- 协作式抢占:在这种模式下,当一个高优先级的线程准备执行时,它会请求当前正在执行的低优先级的线程释放CPU。如果低优先级的线程同意,那么它就会停止执行,高优先级的线程就会开始执行。如果低优先级的线程不同意,那么它将继续执行,直到它完成或者主动释放CPU。
- 时间片轮转:在这种模式下,每个线程都被分配一个固定的时间片(例如,10毫秒),在这个时间片内,线程可以执行任意数量的指令。当一个线程的时间片用完时,线程调度器会暂停这个线程的执行,并允许下一个线程执行一段时间。这种策略可以避免某些线程长时间占用CPU。
-
重载是在同一个类中,有多个方法名相同,参数列表不同(参数个数不同,参数类型不同),与方法的返回值无关,与权限修饰符无关。
-
Spring本身并不直接管理事务,而是提供了事务管理器接口,对于不同的框架或者数据源则用不同的事务管理器;而对于事务,它把相关的属性都封装到一个实体里边去,有以下的属性:
int propagationBehavior; //事务的传播行为 int isolationLevel; //事务隔离级别 int timeout; //事务完成的最短时间 boolean readOnly; //是否只读
Spring提供了对编程式事务和声明式事务的支持,编程式事务是嵌在业务代码中的,而声明式事务是基于xml文件配置。
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。 PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。 PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。 PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
-
集合中线程安全的类有:vector,stack,hashtable,enumeration
-
反射带来的效率问题主要是动态解析类,JVM没法对反射代码优化。
-
**equals()**相等的两个对象hashCode()一定相等。
**hashCode()**相等的两个对象equal()不一定相等。
-
**JSP **(JavaServer Pages)内置对象和属性列举如下:
- request对象 :客户端的请求信息被封装在request对象中,通过它才能了解到客户的需求,然后做出响应。它是HttpServletRequest类的实例。
- response对象:response对象包含了响应客户请求的有关信息,但在JSP中很少直接用到它。它是HttpServletResponse类的实例。
- session对象:session对象指的是客户端与服务器的一次会话,从客户连到服务器的一个WebApplication开始,直到客户端与服务器断开连接为止。它是HttpSession类的实例。
- out对象 :out对象是JspWriter类的实例,是向客户端输出内容常用的对象。
- page对象:page对象就是指向当前JSP页面本身,有点象类中的this指针,它是java.lang.Object类的实例 。
- application对象:application对象实现了用户间数据的共享,可存放全局变量。它开始于服务器的启动,直到服务器的关闭,在此期间,此对象将一直存在;这样在用户的前后连接或不同用户之间的连接中,可以对此对象的同一属性进行操作;在任何地方对此对象属性的操作,都将影响到其他用户对此的访问。服务器的启动和关闭决定了application对象的生命。它是ServletContext类的实例。
- exception对象:exception对象是一个例外对象,当一个页面在运行过程中发生了例外,就产生这个对象。如果一个JSP页面要应用此对象,就必须把isErrorPage设为true,否则无法编译。他实际上是java.lang.Throwable的对象。
- pageContext对象:pageContext对象提供了对JSP页面内所有的对象及名字空间的访问,也就是说他可以访问到本页所在的SESSION,也可以取本页面所在的application的某一属性值,他相当于页面中所有功能的集大成者,它的本 类名也叫pageContext。
- config对象:config对象是在一个Servlet初始化时,JSP引擎向它传递信息用的,此信息包括Servlet初始化时所要用到的参数(通过属性名和属性值构成)以及服务器的有关信息(通过传递一个ServletContext对象)。
-
抽象类和接口都不能被实例化 。
-
在实现类中实现接口时,方法的名字、返回值类型、参数的个数及类型必须与接口中的完全一致,并且必须实现接口中的所有方法。
实现需要是public方法。
-
对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。这句话可以表达得更通俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那么这两个类必定不相等。接口类是一种特殊类,因此对于同一接口不同的类装载器装载所获得的类是不相同的。
Java程序的类加载器采用双亲委派模型,实现双亲委派的代码集中在 java.lang.ClassLoader的loadClass()方法中,此方法实现的大致逻辑 是:先检查是否已经被加载,若没有加载则调用父类加载器loadClass() 方法,若父类加载器为空则默认使用启动类加载器作为父类加载器。如 果父类加载失败,抛出ClassNotFoundException异常。
应用程序类加载器(Application ClassLoader)负责加载用户类路径 (ClassPath)上所指定的类库,不是所有的ClassLoader都加载此路 径。
-
字符串在java中存储在字符串常量区中。
-
Thread可以被继承,用于创建新的线程
Number类可以被继承,Integer,Float,Double等都继承自Number类
ClassLoader可以被继承,用户可以自定义类加载器
-
定义在方法中的局部变量,使用前必须初始化,否则就会出现错误。
当我们新建一个对象时,Java会在Heap中申请一块内存区域用以存放类的数据。而成员变量就是类的数据,也是放在这块内存区域中的。初始化时只需要JVM在申请内存的时候顺便把整块区域都置为零即可完成。所以JVM会自动帮我们完成全局变量的初始化。
对于方法的局部变量,是在线程的Stack中,虽然Stack可以帮我们完成初始化,但这样是存在问题的,比如有些局部变量,在方法的一开始是没有的,有些在循环中的局部变量是要反复的声明多次的。有些局部变量作用域结束后,另一个局部变量又会占用这个局部变量的位置。
局部变量不自动初始化是一种约束,它会尽最大程度减少使用者犯错的可能。假使局部变量可以使用默认值,可能总会无意间忘记赋值,进而导致不可预期的情况出现。
-
指出下列程序运行的结果:tarena and gbc
public class Example{ String str=new String("tarena"); char[]ch={'a','b','c'}; public static void main(String args[]){ Example ex=new Example(); ex.change(ex.str,ex.ch); System.out.print(ex.str+" and "); System.out.print(ex.ch); } public void change(String str,char ch[]){ //引用类型变量,传递的是地址,属于引用传递。 str="test ok"; ch[0]='g'; } }
-
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。 t.join(); //使调用线程 t 在此之前执行完毕。 t.join(1000); //等待 t 线程,等待时间是1000毫秒.
-
String类是不可变类,一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。
StringBuffer对象则代表一个字符序列可变的字符串,当一个StringBuffer被创建以后,通过StringBuffer提供的append()、insert()、reverse()、setCharAt()、setLength()等方法可以改变这个字符串对象的字符序列。一旦通过StringBuffer生成了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象。