Android,Java知识点
⾃⼰⽤来记录,不是很完整,有需要可参考
Android岗位常问java知识点
⼀、知识点
1、⾯向对象理解,特点
封装、继承、多态
万物皆对象
封装:把客观事物封装成抽象的类,并且类可以把⾃⼰的数据和⽅法只让可信的类或者对象操作,对不可信的进⾏信息隐藏。
继承:⼀个类继承⼀个类时候,它可以使⽤现有类的所有功能,并在⽆需重新编写原来的类的情况下对这些功能进⾏扩展。
多态:所谓多态就是指⼀个类实例的相同⽅法在不同情形有不同表现形式。多态机制使具有不同内部结
构的对象可以共享相同的外部接⼝。
不同对象的具体操作不同,但通过⼀个公共的类,它们(那些操作)可以通过相同的⽅式予以调⽤。例如将⼦类传⼊⽗类参数中,运⾏时调⽤⽗类⽅法时通过传⼊的⼦类决定具体的内部结构或⾏为。
接⼝与抽象类的区别
相同点
(1)都不能被实例化 (2)接⼝的实现类或抽象类的⼦类都只有实现了接⼝或抽象类中的⽅法后才能实例化。
不同点
(1)接⼝只有定义,不能有⽅法的实现,java 1.8中可以定义default⽅法体,⽽抽象类可以有定义与实现,⽅法可在抽象类中实现。
(2)实现接⼝的关键字为implements,继承抽象类的关键字为extends。⼀个类可以实现多个接⼝,但⼀个类只能继承⼀个抽象类。所以,使⽤接⼝可以间接地实现多重继承。
(3)接⼝强调特定功能的实现,⽽抽象类强调所属关系。
(4)接⼝成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员⽅法都是public、abstract的。抽象类中成员变量默认default,可在⼦类中被重新定义,也可被重新赋值;抽象⽅法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。
2、Final
(1)修饰类
当⽤final修饰⼀个类时,表明这个类不能被继承。
在使⽤final修饰类的时候,要注意谨慎选择,除⾮这个类真的在以后不会⽤来继承或者出于安全的考虑,尽量不要将类设计为final类。
(2)修饰⽅法
final修饰⼀个⽅法,不能被重写
“使⽤final⽅法的原因有两个。第⼀个原因是把⽅法锁定,以防任何继承类修改它的含义;第⼆个原因是效率。在早期的Java实现版本中,会将final⽅法转为内嵌调⽤。但是如果⽅法过于庞⼤,可能看不到内嵌调⽤带来的任何性能提升。在最近的Java版本中,不需要使⽤final⽅法进⾏这些优化了。“
因此,如果只有在想明确禁⽌ 该⽅法在⼦类中被覆盖的情况下才将⽅法设置为final的。
注:类的private⽅法会隐式地被指定为final⽅法。
(3)修饰变量
对于⼀个final变量,如果是基本数据类型的变量,则其数值⼀旦在初始化之后便不能更改(必须初始化);如果是引⽤类型的变量,则在对其初始化之后便不能再让其指向另⼀个对象。
3、线程同步的⽅法
对于多线程的程序来说,同步指的是在⼀定的时间内只允许某⼀个线程访问某个资源
(1)互斥锁
互斥锁 是最常见的线程同步⽅式,它是⼀种特殊的变量,它有 lock 和 unlock 两种状态,⼀旦获取,就会上锁,且只能由该线程解锁,期间,其他线程⽆法获取。
在使⽤同⼀个资源前加锁,使⽤后解锁,即可实现线程同步,需要注意的是,如果加锁后不解锁,会造成死锁。
(2)读写锁
读写锁处于写锁定的状态,则在解锁之前,所有试图加锁的线程都会阻塞。
读写锁处于读锁定的状态,则所有试图以读模式加锁的线程都可得到访问权,但是以写模式加锁的线程则会阻塞;
(3)信号量
它允许多个线程在同⼀时刻访问同⼀资源,但是需要限制在同⼀时刻访问此资源的最⼤线程数⽬
(4)条件变量
当线程在等待某些满⾜条件时使线程进⼊睡眠状态,⼀旦条件满⾜,就唤醒,这样不会占⽤宝贵的互斥对象锁,实现⾼效
4、volatile
⽤volatile修饰的变量对所有线程的可见性。
5、ArrayList内部结构,与LinkList区别
(1).ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
(2).对于随机访问get和set,ArrayList优于LinkedList,因为ArrayList可以随机定位,⽽LinkedList要移动指针⼀步⼀步的移动到节点处。(参考数组与链表来思考)
(3).对于新增和删除操作add和remove,LinedList⽐较占优势,只需要对指针进⾏修改即可,⽽ArrayList要移动数据来填补被删除的对象的空间。
6、类对象、实例对象
(1)类对象:就是类本⾝
(2)实例对象: 由类实例化出来的对象
7、静态变量和实例变量
(1)静态变量:静态变量也叫做类变量,独⽴于⽅法之外的变量,有static修饰。
静态变量不属于某个实例对象,⽽是属于整个类。只要程序加载了类的字节码,不⽤创建任何实例对象,静态变量就回被分配空间,静态变量就可以被使⽤了。
(2)实例变量:实例变量同样独⽴也是独⽴于⽅法之外 的变量,但没有static修饰。
实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使⽤这个实例变量。
8、浅拷贝与深拷贝
(1)浅拷贝:增加了⼀个指针指向已存在的内存地址
(2)深拷贝:增加了⼀个指针并且申请了⼀个新的内存,使这个增加的指针指向这个新的内存
9、HashMap
10、多线程相关
⼀条线程指的是进程中⼀个单⼀顺序的控制流,⼀个进程中可以并发多个线程,每条线程并⾏执⾏不同的任务。
多线程是多任务的⼀种特别的形式,但多线程使⽤了更⼩的资源开销。
进程:⼀个进程包括由操作系统分配的内存空间,包含⼀个或多个线程。⼀个线程不能独⽴的存在,
它必须是进程的⼀部分。⼀个进程⼀直运⾏,直到所有的⾮守护线程都结束运⾏后才能结束。
多线程能满⾜程序员编写⾼效率的程序来达到充分利⽤ CPU 的⽬的。
11、JVM内存模型和类加载机制。
12、OOM异常相关
内存溢出:当前占⽤的内存+申请的内存资源超过Dalvik虚拟机的最⼤内存
内存抖动:短时间创建⼤量对象,然后再次释放,触发GC
内存泄露:进程中对象未被引⽤,但间接引⽤到其他对象,使GC⽆法对其起作⽤
(1)堆内存溢出
堆内存⽤来存储对象实例,只要不停的创建对象,且GC Roots和对象之间有可达路径避免垃圾回收,那么对象数量在超过最⼤堆的⼤⼩限制后很快会出现。
(2)⽅法区和元空间溢出
(3)直接内存溢出
直接内存并不是虚拟机运⾏时数据区域的⼀部分,并且不受堆内存的限制,但是受到机器内存⼤⼩的限制。常见的⽐如在NIO中可以使⽤native函数直接分配堆外内存就容易导致OOM的问题。
(4)栈内存溢出
栈是线程私有,它的⽣命周期和线程相同。每个⽅法在执⾏的同时都会创建⼀个栈帧⽤于存储局部变量表、操作数栈、动态链接、⽅法出⼝等信息,⽅法调⽤的过程就是栈帧⼊栈和出栈的过程。
在java虚拟机规范中,对虚拟机栈定义了两种异常:
如果线程请求的栈深度⼤于虚拟机所允许的深度,将抛出StackOverflowError异常
如果虚拟机栈可以动态扩展,并且扩展时⽆法申请到⾜够的内存,抛出OutOfMemoryError异常
13、设计模式
单例模式、构造者模式、责任链模式
14、Java堆、栈和队列的区别
栈:先进后出,具有记忆能⼒,使⽤于括号求解
队列:先进先出,以进⾏有顺序的处理,如计算机系统中各种资源的管理
系统⼀般在内存中划分出两种不同的内存空间,⼀种是Stack(栈),⼀种是heap(堆)
它们的主要区别是:
stack是有结构的,每个区块按照⼀定次序存放,可以明确知道每个区块的⼤⼩;heap是没有结构的,数据可以任意存放。因此,stack的寻址速度要快于heap。
每个线程分配⼀个stack,每个进程分配⼀个heap,也就是说,stack是线程独占的,heap是线程共⽤的。
实例化类和实例化对象15、线程的5个状态
(1)新建:new Thread()
(2)就绪:“可执⾏状态”,thread.start()
(3)运⾏:线程获取CPU权限进⾏执⾏,线程只能从就绪转到运⾏
(4)阻塞:线程因为某种原因放弃CPU使⽤权,暂时停⽌运⾏,分为3种情况:
(01) 等待阻塞 – 通过调⽤线程的wait()⽅法,让线程等待某⼯作的完成。
(02) 同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占⽤),它会进⼊同步阻塞状态。
(03) 其他阻塞 – 通过调⽤线程的sleep()或join()或发出了I/O请求时,线程会进⼊到阻塞状态。当sleep()状态超时、join()等待线程终⽌或者超时、或者I/O处理完毕时,线程重新转⼊就绪状态。
(5)死亡:线程执⾏完了或者因异常退出了run()⽅法,该线程结束⽣命周期。
16、String、StringBuffer和StringBuilder的区别
String:不可变字符串
StringBuffer:可变字符串、效率低、线程安全
StringBuilder:可变字符序列、效率⾼、线程不安全
String为字符串常量,⽽StringBuilder和StringBuffer均为字符串变量,即String对象⼀旦创建之后该对象是不可更改的,但后两者能够被多次的修改,并且不产⽣新的未使⽤对象。
常见的:s=s+“aa”;
这种实际上是JVM新建了⼀个叫s的对象,将原来的s加上“aa”赋值给新的s,s实际上并没有被更改,所以,Java中对String对象进⾏的操作实际上是⼀个不断创建新的对象并且将旧的对象回收的⼀个过程,所以执⾏速度很慢。
StringBuilder 类和 StringBuffer 之间的最⼤不同在于 StringBuilder 的⽅法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,多数情况下建议使⽤ StringBuilder类。然⽽在应⽤程序要求线程安全的情况下,则必须使⽤ StringBuffer 类。
17、数组和链表的区别?Java 中有哪些数据结构是⽤它们实现的?
(1)数组的内存空间是连续的,创建数组的时候就会指定数组⼤⼩,且不能动态更改数组的⼤⼩,是因为创建时候已经分配了连续的固定内存空间,每个元素占⽤两个字节。
数组的随机访问速度快,增加和删除效率低。
链表的增删效率⾼,⽽且链表拓展性强
18、重载和重写
(1)重载:⽅法名不变,返回值和参数变
(2)重写:⼦类继承⽗类,重写继承⽗类的⽅法内容
19、数据库索引结构
20、String为什么是final修饰的
String类是⽤final关键字修饰,这说明String不可继承
为了线程安全
21、Int与Integer
Integer是int的包装类;int是基本数据类型;
Integer变量必须实例化后才能使⽤;int变量不需要;
Integer实际是对象的引⽤,指向此new的Integer对象;int是直接存储数据值 ;
Integer的默认值是null;int的默认值是0。
Integer可以区分出未赋值和值为0的区别,int则⽆法表达出未赋值的情况
22、synchronized修饰静态⽅法和普通⽅法的区别
synchronized具有同步功能,是⼀种互斥锁,锁的是对象,synchronized修饰普通⽅法时,锁对象是this对象。修饰静态⽅法时,锁对象是字节码⽂件对象。
synchronized的缺点:
1、synchronized底层是由jvm实现,因此不能⼿动控制锁的释放,不如lock锁灵活,synchronized修饰的⽅法⼀旦出现异常,jvm保证锁会被释放(lock锁需要在finally中释放)。
2、synchronized是⾮公平锁,不保证公平性。
关键字 synchronized 可以保证在同⼀个时刻,只有⼀个线程可以执⾏某个⽅法或者某个代码块(主要是对⽅法或者代码块中存在共享数据的操作)。
synchronized 还可以保证⼀个线程的变化(主要是共享数据的变化)被其他线程所看到(保证可见性,完全可以替代 volatile 功能,但是volatile 更轻量,还是要分场景使⽤)
synchronized 包括三种⽤法:
修饰实例⽅法
修饰静态⽅法
修饰代码块
22、进程和线程的区别
地址空间:线程共享本进程的地址空间,⽽进程之间是独⽴的地址空间。
资源:线程共享本进程的资源如内存、I/O、cpu等,不利于资源的管理和保护,⽽进程之间的资源是独⽴的,能很好的进⾏资源管理和保护。
健壮性:多进程要⽐多线程健壮,⼀个进程崩溃后,在保护模式下不会对其他进程产⽣影响,但是⼀个线程崩溃整个进程都死掉。
执⾏过程:
每个独⽴的进程有⼀个程序运⾏的⼊⼝、顺序执⾏序列和程序⼊⼝,执⾏开销⼤。但是线程不能独⽴
执⾏,必须依存在应⽤程序中,由应⽤程序提供多个线程执⾏控制,执⾏开销⼩。
可并发性:
两者均可并发执⾏。
切换时:
进程切换时,消耗的资源⼤,效率⾼。所以涉及到频繁的切换时,使⽤线程要好于进程。同样如果要求同时进⾏并且⼜要共享某些变量的并发操作,只能⽤线程不能⽤进程。
23、SharePreference原理及跨进程数据共享的问题
SharedPreferences是Android提供的数据持久化的⼀种⼿段,适合单进程、⼩批量的数据存储与访问。因为SharedPreferences的实现是基于单个xml⽂件实现的,并且,所有持久化数据都是⼀次性加载到内存,如果数据过⼤,是不合适采⽤SharedPreferences存放的。⽽适⽤的场景是单进程的原因同样如此,由于Android原⽣的⽂件访问并不⽀持多进程互斥,所以SharePreferences也不⽀持,如果多个进程更新同⼀个xml⽂件,就可能存在同不互斥问题。
⼆、算法
1、翻转单链表
public class Main {
public static void main(String[] args) {
Node head=new Node(0);
Node n1=new Node(1);
Node n2=new Node(2);
Node n3=new Node(3);
head.setNext(n1);n1.setNext(n2);n2.setNext(n3);
//打印反转前的
System.out.println("反转前的链表:");
Node h=head;
while (h!=null){
System.out.Data()+" ");
Next();
}
// System.out.println("递归(从后向前)反转后的链表:");
// head=Reversel(head);
// while (head!=null){
// System.out.Data()+" ");
// Next();
// }
System.out.println("从前向后反转后的链表:");
head=Reversel2(head);
while (head!=null){
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论