Java考题知识点
挑战10个最难回答的Java面试题(附答案) - 里奥ii的文章 - 知乎 https://zhuanlan.zhihu.com/p/79186037
1.java的基本编程单元是类,基本存储单元是变量;
2.对于.java文件而言,一个文件中可以有多个类,但是只能有一个class和.java的文件一致。同时一旦这个.java文件中存在外部的public类,那么这么外部的public类的类名必须和.java的文件名一致。但是考虑到内部类,一个.java文件中可以有多个内部public类,这些的名字也都和.java的名字一样。
3.AOP 和 OOP的区别:
(1). 面向方面编程 AOP 偏重业务处理过程的某个步骤或阶段,强调降低模块之间的耦合度,使代码拥有更好的移植性。
(2). 面向对象编程 (oop) 则是对业务分析中抽取的实体进行方法和属性的封装。
也可以说 AOP 是面向业务中的动词领域, OOP 面向名词领域。
AOP 的一个很重要的特点是源代码无关性,也就是说如果我们的系统中引用了 AOP 组件,即使我们把该组件去掉,系统代码也应该能够编译通过。要实现这一点,可以使用动态 proxy 模式。
4.java 语言使用的字符码集是 Unicode。 Unicode(统一码 、万国码、单一码)是计算机科学领域里的一项业界标准,包括字符集、编码方案等。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制 编码,以满足跨语言、跨平台进行文本转换、处理的要求。Unicode是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。目前的Unicode字符分为17组编排,0x0000 至 0x10FFFF,每组称为平面(Plane),而每平面拥有65536个码位,共1114112个。然而目前只用了少数平面。UTF-8 、UTF-16 、UTF-32 都是将数字转换到程序数据的编码方案。
5.(1)HashMap和Hashtable两个类都实现了Map接口,二者保存K-V对(key-value对) (2):HashTable不允许null值(key和value都不可以),HashMap允许null值(key和value都可以)。 (3):Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。 (4):由所有HashMap类的“collection 视图方法”所返回的迭代器都是快速失败的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器本身的 remove 方法,其他任何时间任何方式的修改,迭代器都将抛出ConcurrentModificationException。Hashtable和HashMap的区别主要是前者是同步的,后者是快速失败机制保证 .
6.(1).子父类存在同名成员时,子类中默认访问子类的成员,可通过super指定访问父类的成员,格式:super.xx (注:xx是成员名);
(2).创建子类对象时,默认会调用父类的无参构造方法,可通过super指定调用父类其他构造方法,格式:s uper(yy) (注:yy是父类构造方法需要传递的参数)
7. Java中的多线程是一种抢占式的机制,而不是分时机制。抢占式的机制是有多个线程处于可运行状态,但是只有一个线程在运行。 wait()和sleep() 共同点 : (1). 他们都是在多线程的环境下,都可以在程序的调用处阻塞指定的毫秒数,并返回。 (2). wait()和sleep()都可以通过interrupt()方法 打断线程的暂停状态 ,从而使线程立刻抛出InterruptedException。 如果线程A希望立即结束线程B,则可以对线程B对应的Thread实例调用interrupt方法。如果此刻线程B正在wait/sleep/join,则线程B会立刻抛出InterruptedException,在catch() {} 中直接return即可安全地结束线程。 需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用 interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到 wait()/sleep()/join()后,就会立刻抛出InterruptedException 。 不同点 : (1).每个对象都有一个锁来控制同步访问。Synchronized关键字可以和对象的锁交互,来实现线程的同步。 sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。 (2).wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用 (3).sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常 (4).sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。 (5).wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
(1).sleep()方法 在指定时间内让当前正在执行的线程暂停执行,但不会释放“锁标志”。不推荐使用。 sleep()使当前线程进入阻塞状态,在指定时间内不会执行。 (2).wait()方法 在其他线程调用对象的notify或notifyAll方法前,导致当前线程等待。线程会释放掉它所占有的“锁标志”,从而使别的线程有机会抢占该锁。 当前线程必须拥有当前对象锁。如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常。 唤醒当前对象锁的等待线程使用notify或notifyAll方法,也必须拥有相同的对象锁,否则也会抛出IllegalMonitorStateException异常。
waite()和notify()必须在synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non-synchronized block中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的常。
(3).yield方法 暂停当前正在执行的线程对象。 yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。 yield()只能使同优先级或更高优先级的线程有执行的机会。
(4).join方法 等待该线程终止。 等待调用join方法的线程结束,再继续执行。如:t.join();//主要用于等待t线程运行结束,若无此句,main则会执行完毕,导致结果不可预测。
8.管道实际上是一种固定大小的缓冲区,管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在于内存中。它类似于通信中半双工信道的进程通信机制,一个管道可以实现双向 的数据传输,而同一个时刻只能最多有一个方向的传输,不能两个方向同时进行。管道的容 量大小通常为内存上的一页,它的大小并不是受磁盘容量大小的限制。当管道满时,进程在 写管道会被阻塞,而当管道空时,进程读管道会被阻塞。
9.方法的重写(override)两同两小一大原则:
方法名相同,参数类型相同 子类返回类型小于等于父类方法返回类型, 子类抛出异常小于等于父类方法抛出异常, 子类访问权限大于等于父类方法访问权限。
10. 类的加载是由类加载器完成的,类加载器包括:根加载器( BootStrap )、扩展加载器( Extension )、系统加载器( System )和用户自定义类加载器( java.lang.ClassLoader 的子类)。从 Java 2 ( JDK 1.2 )开始,类加载过程采取了父亲委托机制( PDM )。 PDM 更好的保证了 Java 平台的安全性,在该机制中, JVM 自带的 Bootstrap 是根加载器,其他的加载器都有且仅有一个父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加载。 JVM 不会向 Java 程序提供对 Bootstrap 的引用。下面是关于几个类加载器的说明: ? Bootstrap :一般用本地代码实现,负责加载 JVM 基础核心类库( rt.jar ); ? Extension :从 java.ext.dirs 系统属性所指定的目录中加载类库,它的父加载器是 Bootstrap ; ? system class loader :又叫应用类加载器,其父类是 Extension 。它是应用最广泛的类加载器。它从环境变量 classpath 或者系统属性 java.class.path 所指定的目录中记载类,是用户自定义加载器的默认父加载器。 ? 用户自定义类加载器: java.lang.ClassLoader 的子类 父类委托机制是可以修改的,有些服务器就是自定义类加载器优先的。
11.被final修饰的变量是常量 大小规则,从小到大 byte short int long float double char 引用类型转换: 向上转型: 向下转型:
12.(1)、一个子类只能继承一个抽象类(虚类),但能实现多个接口; (2)、一个抽象类可以有构造方法,接口没有构造方法; (3)、一个抽象类中的方法不一定是抽象方法,即其中的方法可以有实现(有方法体),接口中的方法都是抽象方法,不能有方法体,只有声明; (4)、一个抽象类可以是public、private、protected、default, 接口只有public; (5)、一个抽象类中的方法可以是public、private、protected、default, 接口中的方法只能是public和default
13.类的加载顺序。
(1) 父类静态代码块(包括静态初始化块,静态属性,但不包括静态方法) (2) 子类静态代码块(包括静态初始化块,静态属性,但不包括静态方法 ) (3) 父类非静态代码块( 包括非静态初始化块,非静态属性 ) (4) 父类构造函数 (5) 子类非静态代码块 ( 包括非静态初始化块,非静态属性 ) (6) 子类构造函数
14. 重载与重写: 首先,重载和重写都是多态的一种体现方式。重载是编译期间的活动,重写是运行期间的活动。 ???? 其次,重载是在一个类中定义相同的名字的方法,方法的参数列表或者类型要互相不同,但是返回值类型不作为是否重载的标准,可以修改可见性; ???? 重写是不同的,要求子类重写基类的方法时要与父类方法具有相同的参数类型和返回值,可见性需要大于等于基类的方法
15.在java中一个unicode占2个字节(byte)。 一个字节等于8比特位(bit)。 所以每个Unicode码占用16个比特位。
16.-Xmx10240m:代表最大堆
-Xms10240m:代表最小堆
-Xmn5120m:代表新生代
-XXSurvivorRatio=3:代表Eden:Survivor = 3 根据Generation-Collection算法(目前大部分JVM采用的算法),一般根据对象的生存周期将堆内存分为若干不同的区域,一般情况将新生代分为Eden ,两块Survivor; 计算Survivor大小, Eden:Survivor = 3,总大小为5120,3x+x+x=5120 x=1024
17.Servlet的生命周期 (1).加载:容器通过类加载器使用Servlet类对应的文件来加载Servlet (2).创建:通过调用Servlet的构造函数来创建一个Servlet实例 (3).初始化:通过调用Servlet的init()方法来完成初始化工作,这个方法是在Servlet已经被创建,但在向客户端提供服务之前调用。 (4).处理客户请求:Servlet创建后就可以处理请求,当有新的客户端请求时,Web容器都会创建一个新的线程来处理该请求。接着调用Servlet的 Service()方法来响应客户端请求(Service方法会根据请求的method属性来调用doGet()和doPost()) (5).卸载:容器在卸载Servlet之前需要调用destroy()方法,让Servlet释放其占用的资源。
18.(1)线程安全概念: 如果代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。 线程安全问题都是由全局变量及静态变量引起的。 若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
LinkedList 和 ArrayList 都是不同步的,线程不安全; Vector 和 Stack 都是同步的,线程安全; Set是线程不安全的;
Hashtable的方法是同步的,线程安全; HashMap的方法不是同步的,线程不安全;
(2)Vector & ArrayList 的主要区别 1) 同步性:Vector是线程安全的,也就是说是同步的 ,而ArrayList 是线程序不安全的,不是同步的 数2。 2)数据增长:当需要增长时,Vector默认增长为原来一倍 ,而ArrayList却是原来的50% ,这样,ArrayList就有利于节约内存空间。 如果涉及到堆栈,队列等操作,应该考虑用Vector,如果需要快速随机访问元素,应该使用ArrayList 。
(3)扩展知识: 1). Hashtable & HashMap Hashtable和HashMap它们的性能方面的比较类似 Vector和ArrayList,比如Hashtable的方法是同步的,而HashMap的不是。 2). ArrayList & LinkedList ArrayList的内部实现是基于内部数组Object[],所以从概念上讲,它更象数组,但LinkedList的内部实现是基于一组连接的记录,所以,它更象一个链表结构,所以,它们在性能上有很大的差别: 从上面的分析可知,在ArrayList的前面或中间插入数据时,你必须将其后的所有数据相应的后移,这样必然要花费较多时间,所以,当你的操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能; 而访问链表中的某个元素时,就必须从链表的一端开始沿着连接方向一个一个元素地去查找,直到找到所需的元素为止,所以,当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。
19.(1). 并发:在 操作系统 中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个 处理机 上运行。其中两种并发关系分别是同步和互斥 (2). 互斥:进程间相互排斥的使用临界资源的现象,就叫互斥。 (3). 同步: 进程之间的关系不是相互排斥临界资源的关系,而是相互依赖的关系。进一步的说明:就是前一个进程的输出作为后一个进程的输入,当第一个进程没有输出时第二个进程必须等待。具有同步关系的一组并发进程相互发送的信息称为消息或事件。 其中并发又有伪并发和真并发,伪并发是指单核处理器的并发,真并发是指多核处理器的并发。 (4). 并行:在单处理器中多道程序设计系统中,进程被交替执行,表现出一种并发的外部特种;在多处理器系统中,进程不仅可以交替执行,而且可以重叠执行。在多处理器上的程序才可实现并行处理。从而可知,并行是针对多处理器而言的。并行是同时发生的多个并发事件,具有并发的含义,但并发不一定并行,也亦是说并发事件之间不一定要同一时刻发生。 (5). 多线程:多线程是程序设计的逻辑层概念,它是进程中并发运行的一段代码。多线程可以实现线程间的切换执行。 (6). 异步:异步和同步是相对的,同步就是顺序执行,执行完一个再执行下一个,需要等待、协调运行。异步就是彼此独立,在等待某事件的过程中继续做自己的事,不需要等待这一事件完成后再工作。线程就是实现异步的一个方式。异步是让调用方法的主线程不需要同步等待另一线程的完成,从而可以让主线程干其它的事情。 异步和多线程并不是一个同等关系,异步是最终目的,多线程只是我们实现异步的一种手段。异步是当一个调用请求发送给被调用者,而调用者不用等待其结果的返回而可以做其它的事情。实现异步可以采用多线程技术或则交给另外的进程来处理。
20. Math.ceil(d1) ceil :如果参数小于0且大于-1.0,结果为 -0
Math.floor(d1) floor :如果是 -0.0,那么其结果是 -0.0 ;
21.异常:(简答题 (必考)// 程序运行题 finally 一定执行) throwable: erro:不管 exception: runtimexception:运行时异常,不建议使用异常处理机制来处理。 otherexception:检查性异常,必须使用异常处理机制来处理。 try:捕获异常 catch:(异常类型 e) :处理异常,catch可以有多个,要求子类异常在上,父类异常在下 finally:一定会执行 throws:抛出异常方案,在方法体外使用 throw:抛异常,主动抛出希望处理的异常,方法体内使用 异常分为运行时异常,非运行时异常和error,其中error是系统异常,只能重启系统解决。非运行时异常需要我们自己补获,而运行异常是程序运行时由虚拟机帮助我们补获,运行时异常包括数组的溢出,内存的溢出空指针,分母为0等。
22. java.io.Serializable接口是一个标志性接口,在接口内部没有定义任何属性与方法。只是用于标志此接口的实现类可以被序列化与反序列化。
java.lang.Cloneable接口是一个标志性接口,在接口内部没有定义任何属性与方法。以指示Object.clone()方法可以合法地对该类实例进行按字段复制。
java.lang.CharSequence接口对许多不同种类的char序列提供统一的只读访问接口。CharSequence是char值的一个可读序列。
java.lang.Comparable接口,此接口强制对实现它的每个类的对象进行整体排序,此序列被称为该类的自然排序
23.Java 重载的规则: (1)、必须具有不同的参数列表; (2)、可以有不同的返回类型,只要参数列表不同就可以; (3)、可以有不同的访问修饰符; (4)、可以抛出不同的异常; (5)、方法能够在一个类中或者在一个子类中被重载。
方法的重写: (1)、在子类中可以根据需要对从基类中继承来的方法进行重写。 (2)、重写的方法和被重写的方法必须具有相同方法名称、参数列表和返回类型。 (3)、重写方法不能使用比被重写的方法更严格的访问权限。
24. A、Semaphore:类,控制某个资源可被同时访问的个数; B、ReentrantLock:类,具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大; C、 Future:接口,表示异步计算的结果; D、 CountDownLatch: 类,可以用来在一个线程中等待多个线程完成任务的类。
25. jar 将许多文件组合成一个jar文件 javac 编译 javadoc 它从程序源代码中抽取类、方法、成员等注释形成一个和源代码配套的API帮助文档。 javah 把java代码声明的JNI方法转化成C\C++头文件。
26. (1).从地址栏显示来说 forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器. 浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址. redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.
(2).从数据共享来说 forward:转发页面和转发到的页面可以共享request里面的数据. redirect:不能共享数据.
(3).从运用地方来说 forward:一般用于用户登陆的时候,根据角色转发到相应的模块. redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等.
(4).从效率来说 forward:高. redirect:低.
27.集合图 1. List 是一个有序集合,可以存放重复的数据 (有序:存进是什么顺序,取出时还是什么顺序) (1).ArrayList 底层是数组适合查询,不适合增删元素。 (2).LiskedList 底层是双向链表适合增删元素,不适合查询操作。 (3).Vector 底层和ArrayList相同,但是Vector是线程安全的,效率较低很少使用 2. Set 是一个无序集合,不允许放重复的数据 (无序可重复,存进和取出的顺序不一样) (1).HashSet 底层是哈希表/散列表 (2).TreeSet 继承sartedSet接口(无需不可重复,但存进去的元素可以按照元素的大小自动排序) 3. Map 是一个无序集合,以键值对的方式存放数据,键对象不允许重复,值对象可以重复。 (1).HashMap实现不同步,线程不安全。 HashTable线程安全 (2).HashMap中的key-value都是存储在Entry中的。 (3).HashMap可以存null键和null值,不保证元素的顺序恒久不变,它的底层使用的是数组和链表,通过hashCode()方法和equals方法保证键的唯一性
28.取值范围和包装类 Java中的四类八种基本数据类型 第一类:整数类型 byte short int long 第二类:浮点型 float double 第三类:逻辑型 boolean(它只有两个值可取true false) 第四类:字符型 char
29. final final关键字
1.final修饰变量,则等同于常量 2.final修饰方法中的参数,称为最终参数。 3.final修饰类,则类不能被继承 4.final修饰方法,则方法不能被重写
30.标识符: 1. 数字,字母,符号(只有_和$两种),数字不能开头。 2. 不能是关键字(有两个保留关键字,goto,const,关键字都是小写的)或者显式常量(null,true,false)。
31.抽象类 特点:? (1).抽象类中可以构造方法? (2).抽象类中可以存在普通属性,方法,静态属性和方法。? (3).抽象类中可以存在抽象方法。? (4).如果一个类中有一个抽象方法,那么当前类一定是抽象类;抽象类中不一定有抽象方法。? (5).抽象类中的抽象方法,需要有子类实现,如果子类不实现,则子类也需要定义为抽象的。? (6),抽象类不能被实例化,抽象类和抽象方法必须被abstract修饰
关键字使用注意:? 抽象类中的抽象方法(其前有abstract修饰)不能用private、static、synchronized、native访问修饰符修饰。
接口 (1).在接口中只有方法的声明,没有方法体。? (2).在接口中只有常量,因为定义的变量,在编译的时候都会默认加上public static final? (3).在接口中的方法,永远都被public来修饰。? (4).接口中没有构造方法,也不能实例化接口的对象。(所以接口不能继承类)? (5).接口可以实现多继承? (6).接口中定义的方法都需要有实现类来实现,如果实现类不能实现接口中的所有方法则实现类定义为抽象类。? (7).接口可以继承接口,用extends
两者的语法区别:
(1).抽象类可以有构造方法,接口中不能有构造方法。 (2).抽象类中可以有普通成员变量,接口中没有普通成员变量 (3).抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。 (4).抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。 (5).抽象类中可以包含静态方法,接口中不能包含静态方法 (6).抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。 (7).一个类可以实现多个接口,但只能继承一个抽象类。
两者在应用上的区别: 接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。而抽象类在代码实现方面发挥作用,可以实现代码的重用,
32.静态 JAVA的初始化顺序: 父类的静态成员初始化>父类的静态代码块>子类的静态成员初始化>子类的静态代码块>父类的代码块>父类的构造方法>子类的代码块>子类的构造方法 注意: 1.静态成员和静态代码块只有在类加载的时候执行一次,再次创建实例时,不再执行,因为只在方法区存在一份,属于一整个类。 2.上述的是通用的加载顺序,如果没有则省略
33. Lanbda表达式的主要作用就是代替匿名内部类的繁琐语法, 它由三部分组成: (1) 形参列表。形参列表允许省略形参类型。如果形参列表中只有一个参数,甚至连形参列表的圆括号也可以省略。 (2) 箭头(→)。必须通过英文中画线和大于符号组成。 (3)代码块。如果代码块只包含一条语句,Lambda表达式允许省略代码块的花括号,那么那条语句就不要用花括号表示语句结束。 Lambda代码块只有一条return语句,甚至可以省略return关键字。 Lambda表达式需要返回值,而它的代码块中仅有一套省略了return的语句。 Lambda表达式会自动返回这条语句的值。
34. (1).静态语句块中x为局部变量,不影响静态变量x的值 (2).和y为静态变量,默认初始值为0,属于当前类,其值得改变会影响整个类运行。 (3).中自增操作非原子性的 main方法中: 执行x--后 x=-1 调用myMethod方法,x执行x++结果为-1(后++),但x=0,++x结果1,x=1 ,则y=0 x+y+ ++x,先执行x+y,结果为1,执行++x结果为2,得到最终结果为3
35. 时间复杂度 、时间复杂度
(1)时间频度
一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。
(2)时间复杂度
在刚才提到的时间频度中,n称为问题的规模,当n不断变化时,时间频度T(n)也会不断变化。但有时我们想知道它变化时呈现什么规律。为此,我们引入时间复杂度概念。
一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
在各种不同算法中,若算法中语句执行次数为一个常数,则时间复杂度为O(1),另外,在时间频度不相同时,时间复杂度有可能相同,如T(n)=n2+3n+4与T(n)=4n2+2n+1它们的频度不同,但时间复杂度相同,都为O(n2)。
按数量级递增排列,常见的时间复杂度有:
常数阶O(1),对数阶O(log2n),线性阶O(n),
线性对数阶O(nlog2n),平方阶O(n2),立方阶O(n3),...,
k次方阶O(nk),指数阶O(2n)。随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低。
2、空间复杂度
与时间复杂度类似,空间复杂度是指算法在计算机内执行时所需存储空间的度量。记作:
S(n)=O(f(n))
我们一般所讨论的是除正常占用内存开销外的辅助存储单元规模 二、常见算法时间复杂度: O(1): 表示算法的运行时间为常量 O(n): 表示该算法是线性算法 O(㏒2n): 二分查找算法 O(n2): 对数组进行排序的各种简单算法,例如直接插入排序的算法。 O(n3): 做两个n阶矩阵的乘法运算 O(2n): 求具有n个元素集合的所有子集的算法 O(n!): 求具有N个元素的全排列的算法 优<---------------------------<劣 O(1)<O(㏒2n)<O(n)<O(n2)<O(2n) 时间复杂度按数量级递增排列依次为:常数阶O(1)、对数阶O(log2n)、线性阶O(n)、线性对数阶O(nlog2n)、平方阶O(n2)、立方阶O(n3)、……k次方阶O(nk)、指数阶O(2n)。 三、算 法的时间复杂度(计算实例) 定义:如果一个问题的规模是n,解这一问题的某一算法所需要的时间为T(n),它是n的某一函数 T(n)称为这一算法的“时间复杂性”。 当输入量n逐渐加大时,时间复杂性的极限情形称为算法的“渐近时间复杂性”。 我们常用大O表示法表示时间复杂性,注意它是某一个算法的时间复杂性。大O表示只是说有上界,由定义如果f(n)=O(n),那显然成立f(n)=O(n^2),它给你一个上界,但并不是上确界,但人们在表示的时候一般都习惯表示前者。 此外,一个问题本身也有它的复杂性,如果某个算法的复杂性到达了这个问题复杂性的下界,那就称这样的算法是最佳算法。 “大O记法”:在这种描述中使用的基本参数是 n,即问题实例的规模,把复杂性或运行时间表达为n的函数。这里的“O”表示量级 (order),比如说“二分检索是 O(logn)的”,也就是说它需要“通过logn量级的步骤去检索一个规模为n的数组”记法 O ( f(n) )表示当 n增大时,运行时间至多将以正比于 f(n)的速度增长。 这种渐进估计对算法的理论分析和大致比较是非常有价值的,但在实践中细节也可能造成差异。例如,一个低附加代价的O(n2)算法在n较小的情况下可能比一个高附加代价的 O(nlogn)算法运行得更快。当然,随着n足够大以后,具有较慢上升函数的算法必然工作得更快。 O(1) Temp=i;i=j;j=temp; 以上三条单个语句的频度均为1,该程序段的执行时间是一个与问题规模n无关的常数。算法的时间复杂度为常数阶,记作T(n)=O(1)。如果算法的执行时 间不随着问题规模n的增加而增长,即使算法中有上千条语句,其执行时间也不过是一个较大的常数。此类算法的时间复杂度是O(1)。 O(n^2) 2.1. 交换i和j的内容 sum=0; (一次) for(i=1;i<=n;i++) (n次 ) for(j=1;j<=n;j++) (n^2次 ) sum++; (n^2次 ) 解:T(n)=2n^2+n+1 =O(n^2) 2.2. for (i=1;i<n;i++) { y=y+1; ① for (j=0;j<=(2*n);j++) x++; ② } 解: 语句1的频度是n-1 语句2的频度是(n-1)*(2n+1)=2n^2-n-1 f(n)=2n^2-n-1+(n-1)=2n^2-2 该程序的时间复杂度T(n)=O(n^2). O(n) 2.3. a=0; b=1; ① for (i=1;i<=n;i++) ② { s=a+b; ③ b=a; ④ a=s; ⑤ } 解: 语句1的频度:2, 语句2的频度: n, 语句3的频度: n-1, 语句4的频度:n-1, 语句5的频度:n-1, T(n)=2+n+3(n-1)=4n-1=O(n). O(log2n ) 2.4. i=1; ① while (i<=n) i=i*2; ② 解: 语句1的频度是1, 设语句2的频度是f(n), 则:2^f(n)<=n;f(n)<=log2n 取最大值f(n)= log2n, T(n)=O(log2n ) O(n^3) 2.5. for(i=0;i<n;i++) { for(j=0;j<i;j++) { for(k=0;k<j;k++) x=x+2; } } 解:当i=m, j=k的时候,内层循环的次数为k当i=m时, j 可以取 0,1,...,m-1 , 所以这里最内循环共进行了0+1+...+m-1=(m-1)m/2次所以,i从0取到n, 则循环共进行了: 0+(1-1)*1/2+...+(n-1)n/2=n(n+1)(n-1)/6所以时间复杂度为O(n^3). 我们还应该区分算法的最坏情况的行为和期望行为。如快速排序的最 坏情况运行时间是 O(n^2),但期望时间是 O(nlogn)。 通过每次都仔细 地选择基准值,我们有可能把平方情况 (即O(n^2)情况)的概率减小到几乎等于 0。在实际中,精心实现的快速排序一般都能以 (O(nlogn)时间运行。 下面是一些常用的记法: 访问数组中的元素是常数时间操作,或说O(1)操作。一个算法如 果能在每个步骤去掉一半数据元素,如二分检索,通常它就取 O(logn)时间。用strcmp比较两个具有n个字符的串需要O(n)时间 。 常规的矩阵乘算法是O(n^3),因为算出每个元素都需要将n对 元素相乘并加到一起,所有元素的个数是n^2。 指数时间算法通常来源于需要求出所有可能结果。例如,n个元 素的集合共有2n个子集,所以要求出所有子集的算法将是O(2n)的 。 指数算法一般说来是太复杂了,除非n的值非常小,因为,在 这个问题中增加一个元素就导致运行时间加倍。不幸的是,确实有许多问题 (如著名 的“巡回售货员问题” ),到目前为止找到的算法都是指数的。 如果我们真的遇到这种情况, 通常应该用寻找近似最佳结果的算法替代之。
36.访问控制权限 1)对于public修饰符,它具有最大的访问权限,可以访问任何一个在CLASSPATH下的类、接口、异常等。它往往用于对外的情况,也就是对象或类对外的一种接口的形式。 (2)对于protected修饰符,它主要的作用就是用来保护子类的。它的含义在于子类可以用它修饰的成员,其他的不可以,它相当于传递给子类的一种继承的东西。 (3)对于default来说,有点的时候也成为friendly(友员),它是针对本包访问而设计的,任何处于本包下的类、接口、异常等,都可以相互访问,即使是父类没有用protected修饰的成员也可以。 (4)对于private来说,它的访问权限仅限于类的内部,是一种封装的体现,例如,大多数的成员变量都是修饰符为private的,它们不希望被其他任何外部的类访问。
37.运行时数据区包括:虚拟机栈区,堆区,方法区,本地方法栈,程序计数器 虚拟机栈区 :也就是我们常说的栈区,线程私有,存放基本类型,对象的引用和 returnAddress ,在编译期间完成分配。 堆区 , JAVA 堆,也称 GC 堆,所有线程共享,存放对象的实例和数组, JAVA 堆是垃圾收集器管理的主要区域。 方法区 :所有线程共享,存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据。这个区域的内存回收目标主要是针对常量池的对象的回收和对类型的卸载。 程序计数器 :线程私有,每个线程都有自己独立的程序计数器,用来指示下一条指令的地址。
38.线程安全的问题全都是由全局变量以及静态变量引起的,非静态自然不会存在线程安全问题。
39.类中变量:除了private权限外,其他权限的变量(没有表示默认default),均可以用“对象.变量名”来调用。对于private变量,即使使用static,也不能用“类.变量名”来调用私有变量。只能通过类中的public get()方法来调用。 类中方法:除了private权限外,其他权限的方法(没有表示默认default),均可以用“对象.方法名”来调用。private方法可以用java反射机制调用。当然如果用 private修饰方法,该方法只在类的内部调用。其中比较著名的就是单例模式中的私有构造方法。 static属性:static方法在编译期就已经生成了,其他方法在运行期生成。非私有的static方法可以用“类.方法名”调用。但是私有的static变量和方法都是不可能被调用的,虽然private static这种写法很少见,但仍然存在,且编译器不会报错。题中static void method2() { }的权限是默认权限,所以可以用“类.方法名”来调用。
40.排序 (1)冒泡 public static void bubbleSort(int[] a) { for (int i = 1; i < a.length; i++) { for (int j = 0; j < a.length - i ; j++) { if (a[j] > a[j + 1]) { int t = a[j]; a[j] = a[j + 1]; a[j + 1] = t; } } } }
(2)选择: int[] arr = {10,3,4,2,999,123,456,768,34,9}; int index ; for(int j = 0;j<10;j++) { index = 0; for(int i = 1;i<=arr.length-1-j;i++ ) { if(arr[index]<=arr[i]) { index = i; } } // System.out.println(index); int temp = 0; temp = arr[index]; arr[index] = arr[arr.length-1-j]; arr[arr.length-1-j] = temp; } for (int i : arr) { System.out.print(i+","); } https://www.cnblogs.com/guoyaohua/p/8600214.html 具体参考
41:(1),Java 关键字列表 (依字母排序 共50组): abstract, assert, boolean, break, byte, case, catch, char, class, const(保留关键字), continue, default, do, double, else, enum, extends, final, finally, float, for, goto(保留关键字), if, implements, import, instanceof, int, interface, long, native, new, package, private, protected, public, return, short, static, strictfp, super, switch, synchronized, this, throw, throws, transient, try, void, volatile, while
(2),保留字列表 (依字母排序 共14组),Java保留字是指现有Java版本尚未使用,但以后版本可能会作为关键字使用: byValue, cast, false, future, generic, inner, operator, outer, rest, true, var, goto (保留关键字) , const (保留关键字) , null
42:String,StringBuild和StringBuffer StringBuffer类的对象调用toString()方法将转换为String类型 ?这个正确 两个类都有append()方法 ?String类没有append方法
可以直接将字符串“test”复制给声明的Stirng类和StringBuffer类的变量 ?引用类型只有String可以直接复制,其他的都要new出来
两个类的实例的值都能够被改变 ??StringBuffer类可以直接改变它的内容,不用重新分配地址; String?对象/ 实例 ? 是不可以被改变的。
String: 是对象不是原始类型. 为不可变对象,一旦被创建,就不能修改它的值. 对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去. String 是final类,即不能被继承.
StringBuffer: 是一个可变对象,当对他进行修改的时候不会像String那样重新建立对象 它只能通过构造函数来建立, StringBuffer sb = new StringBuffer(); !!!:不能通过赋值符号对他进行付值.? sb = "welcome to here!";//error 对象被建立以后,在内存中就会分配内存空间,并初始保存一个null.向StringBuffer 中赋值的时候可以通过它的append方法. sb.append("hello");
字符串连接操作中StringBuffer的效率要比String高: String str = new String("welcome to "); str += "here"; 的处理步骤实际上是通过建立一个StringBuffer,然后调用append(),最后 再将StringBuffer toSting()(toString方法:StringBuffer类型转化成String类型);
43: JAVA的JVM的内存可分为3个区:堆(heap)、栈(stack)和方法区(method)
(1)栈区: 每个线程包含一个栈区,栈中只保存方法中(不包括对象的成员变量)的基础数据类型和自定义对象的引用(不是对象),对象都存放在堆区中。 每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。 栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
(2)堆区: 存储的全部是对象实例,每个对象都包含一个与之对应的class的信息(class信息存放在方法区)。 jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身,几乎所有的对象实例和数组都在堆中分配。
(3)方法区: 又叫静态区,跟堆一样,被所有的线程共享。它用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
44:Math类中提供了三个与取整有关的方法:ceil,floor,round,这些方法的作用于它们的英文名称的含义相对应,例如: ceil的英文意义是天花板,该方法就表示向上取整,Math.ceil(11.3)的结果为12,Math.ceil(-11.6)的结果为-11;floor的英文是地板,该方法就表示向下取整,Math.floor(11.6)的结果是11,Math.floor(-11.4)的结果-12; 最难掌握的是round方法,他表示“四舍五入”,算法为Math.floor(x+0.5),即将原来的数字加上0.5后再向下取整,所以,Math.round(11.5)的结果是12,Math.round(-11.5)的结果为-11.
45: