先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
正文
今天下午去了一个线下面试,笔试+技术面,这是我人生中第一次面着面着心态炸了的面试,结果非常糟糕。现在回想起来,不管是在心态上,还是在基础知识上,我都存在着很多问题。为了直面我自己存在的问题,于是有了这篇复盘文章。
笔试题复盘
下面是在笔试中我回答的不好的题目:
- 重载和重写是什么意思,区别是什么?
- Java异常机制中,异常Exception与错误Error区别
- List能否转为List
- Synchronized的原理以及与ReentrantLock的区别。
- Looper如何在子线程中创建?
- volatile和synchronize有什么区别?
- 请说一说ArrayList 如何保证线程安全,除了加关键字的方式 ?
1.重载和重写是什么意思,区别是什么?
重写(Override)
重写就是重新写的意思,当父类中的方法对于子类来说不适用或者需要扩展增强时,子类可以对从父类中继承来的方法进行重写。
比如Activity是Android开发中四大组件之一。在Activity中存在各种声明周期方法:onCreate、onStart …等等。而我们应用中需要使用Activity来展示UI,那么我们会需要编写自己的类继承自Activity。
public class MainActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
在上述代码中,onCreate就是被我们重写的Activity中定义方法。我们在onCreate增加了setContentView
的调用,完成了对超类Activity中对应方法的修改与扩展。
重载(Overload)
重载则是在同一个类中,允许存在多个同名方法,只要它们的参数列表不同即可。比如在Android开发中,我们会使用LayoutInflater的inflate方法加载布局,inflate方法存在多个定义,其中包括两个参数的,与三个参数的:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
//......
}
可以看到在两个参数的inflate方法中,会为我们调起三个参数的inflate方法,而定义第一个inflate方法的目的就是为了提供默认的attachToRoot
参数值。因为Java中无法定义方法参数默认值,所以我们经常使用重载达成此目的。
2.Java异常机制中,异常Exception与错误Error区别
在Java中存在一个Throwable
可抛出类,Throwable
有两个重要的子类,一个是Error,另一个则是Exception。
Error
是程序不能处理的错误,表示程序中较严重问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError等等。这些错误发生时,JVM一般会选择线程终止。这些错误是不可查的,它们在程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。
Exception
是程序可以处理的异常。而Exception又分为运行时异常(RuntimeException)与非运行时异常。
- 运行异常
运行时异常,又称不受检查异常 。所谓的不受检查,指的是Java编译检查时不会告诉我们有这个异常,需要在运行时候才会暴露出来,比如下标越界,空指针异常等。
- 非运行时异常
RuntimeException之外的异常我们统称为非运行时异常,比如IOException、SQLException,是必须进行处理的异常(检查异常) ,如果不处理(throw到上层或者try-catch),程序就不能编译通过 。
3.List能否转为List
Java的泛型是伪泛型,编译时会进行泛型擦除
因此List和 List 最终的类型都被擦除了,无论是List 还是 List 都是List类型。
既然存在泛型擦除,但是下面的代码无法通过编译检查:
List<String> strs = new ArrayList<Integer>();
List<Object> objects = strs;
编译器会帮我我们检查明显的代码问题,因此上述代码会报错,这是编译器的行为,但是如果我们将代码改为:
List<String> strs = (List)new ArrayList<Integer>();
List<Object> objects = (List)strs;
注意,每条语句我们增加了强转声明。此时编译器能够成功完成编译。因此List其实能够强转为List。但是存在隐患:
List<String> strs = (List)new ArrayList<Integer>();
List<Object> objects = (List)strs;
objects.add(123);
String str = strs.get(0);
上述代码使用objects(List )向集合中增加整型数据:123。然后通过 strs获取数据时,因为其类型为List,但是真实数据类型为整型。此时就会发生运行时异常:
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String
4.Synchronized的原理以及与ReentrantLock的区别。
ReentrantLock与Synchronized的区别,除了一个是Java类实现,一个是关键字之外,还包括:
除此之外,ReenTrantLock相对于Synchronized还拥有自己的独有特性:
- ReenTrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。
- ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程。
- ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制。
5.Looper如何在子线程中创建?
创建子线程的looper必须要通过Looper.prepare()初始化looper,然后再通过Looper.loop()方法让Loop运行起来。
那么具体的细节请看下面的说明:
Handler消息处理流程前面已经介绍过了,还不清楚的朋友,一定要先看下前面章节《Handler怎么进行线程通信,原理是什么》。
首先我们要知道Looper相关的几个重要方法:
Looper.prepare():Looper 初始化, 同时会初始化MessageQueue, Looper消息机制必须要初始化Looper。
Looper.myLooper(): 获取当前调用线程中ThreadLocal缓存的Looper对象。
Looper.loop():让Loop进入死循环。
getLooper(): Handler中的方法, 获取Handler中缓存的Looper对象。
Looper.quit(): 终止 Looper.looper() 死循环, 执行 quit后Handler机制将失效, 执行时如果MessageQueue中还有Message未执行, 将不会执行未执行Message, 直接退出, 调用quit后将不能发消息给Handler。
Looper.quitSafely(): 作用同 quit(), 但终止Looper之前会先执行完消息队列中所有非延时任务, 调用quit后将不能发消息给Handler。
子线程中使用Looper如下:
Handler mHandler;
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();//Looper初始化
//Handler初始化 需要注意, Handler初始化传入Looper对象是子线程中缓存的Looper对象
mHandler = new Handler(Looper.myLooper());
Looper.loop();//死循环
//注意: Looper.loop()之后的位置代码在Looper退出之前不会执行,(并非永远不执行)
}
}).start();
其中主要需要注意的就是Handler的初始化,需要传入当前线程的 Looper 对象。然而,一般情况下我们并不会这样去给子线程创建looper,因为这样的方式只能创建一个handler对象。我们最正确的创建子线程 Looper的方法一般是调用HandlerThread。
6.volatile和synchronize有什么区别?
- volatile 只能作用于变量,synchronized 可以作用于变量、方法、对象。
- volatile 只保证了可见性和有序性,无法保证原子性,synchronized 可以保证线程间的有序性(个人猜测是无法保证线程内的有序性,即线程内的代码可能被 CPU 指令重排序)、原子性和可见性。
- volatile 线程不阻塞,synchronized 线程阻塞。
- volatile 本质是告诉 jvm 当前变量在寄存器中的值是不安全的需要从内存中读取;sychronized 则是锁定当前变量,只有当前线程可以访问到该变量其他线程被阻塞。
- volatile标记的变量不会被编译器优化, synchronized标记的变量可以被编译器优化。
7.请说一说ArrayList 如何保证线程安全,除了加关键字的方式 ?
ArrayList 如何保证线程安全
- 继承Arraylist,然后重写或按需求编写自己的方法,这些方法要写成synchronized,在这些synchronized的方法中调用ArrayList的方法。
- 可以使用Collections.synchronizedList()这个函数,使用方法如下
List<Map<String,Object>> data=new ArrayList<Map<String,Object>>();
List<Map<String,Object>> data=Collections.synchronizedList(new ArrayList<Map<String,Object>>());
- 使用CopyOnWriteArrayList
题外话
不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊
这里我为大家准备了一些我工作以来以及参与过的大大小小的面试收集总结出来的一套进阶学习的视频及面试专题资料包,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家~
欢迎评论区讨论。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
]
欢迎评论区讨论。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-xRgUYBdv-1713228184153)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!