每日任务
2018年3月2日
-Service-和-IntentService-的区别
Service
Service是长期运行在后台的应用程序组件。
Service不是一个单独的进程,它和应用程序在同一个进程中,Service也不算是一个线程,它和线程没有任何关系,所有它不能直接处理耗时操作。如果直接把耗时操作放在Service的onStartCommand()中,很容易引起ANR,如果有耗时操作就必须开启一个单独的线程来处理。
IntentService
IntentService是继承于Service并处理异步请求的一个类,在IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统Service一样,同时,当任务执行完后,IntentService会自动停止,而不需要我们去手动控制。另外,可以启动IntentService多次,而每一个耗时操作都会以工作队列的方式在IntentService的onHandlerIntent回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。
2018年3月3日
阅读记录
复习点
- 阅读:精通比特币-HD协议:
- 阅读:JVM——Java虚拟机架构 :heavy_check_mark:
- 阅读:Kotlin Primer·第二章·基本语法
- 阅读:Android面试一天一题(1 Day):heavy_check_mark:
- 阅读:Android studio代码格式规范
- 阅读:Android进阶笔记09:Android开发经验部分总结:heavy_check_mark:
- 阅读:借腾讯开源 VasDolly,谈谈 Android 签名和多渠道打包的原理!
- 阅读:收集了50道基础的java面试题
- 阅读:Android开源框架源码鉴赏:VirtualAPK
-堆和栈的区别
堆和栈的区别
- 各司其职
最主要的区别就是栈内存是用来存储局部变量和方法调用信息
而堆内存用来存储java中的对象。无论是成员变量、局部变量还是类变量,它们指向的对象都存储在堆内存中。
- 空间大小
栈的内存要远远小于堆内存,如果你使用递归的话,那么你的栈很快就会充满并产生StackOverflowError。
- 独有还是共享
栈内存归属于线程的私有内存,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见。
而堆内存中的对象对所有线程可见,可以被所有线程访问。
- 异常错误
如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常,
如果JVM栈可以动态扩展(大部分JVM是可以的),当扩展时无法申请到足够的内存则抛出outOfmemoryError异常。
而堆内存没有可用的空间存储生成的对象,JVM会抛出java.lang.OutOfMemoryError。
重要复习点
待阅读
2018年3月5日
重要复习点
1. 使用异步加载
2. 使用软引用进行图片的缓存
尺寸压缩之inSampleSize
为了防止加载图片时内存溢出,需要先计算采样率,然后再去加载图片。
- inSampleSiz只能是2个平方,如计算结果是7会按照4进行压缩,计算结果是15会按照8进行压缩。
- 存在两种算法:
算法一:图片长与目标长比,图片宽与目标宽比,取最大值。
算法二:取目标长宽的最大值来计算,这样会减少过度的尺寸压缩。
2018年3月6日
Android-压缩大图到容量超小的图片
压缩图片的宽高
/**
* 计算图片的压缩比
*
* @param options
* @param reqWidth
* @param reqHeight
* @return
*/
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;//压缩比
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / inSampleSize) >= reqHeight &&
(halfWidth / inSampleSize) >= reqWidth) {
inSampleSize*=2;
}
}
return inSampleSize;
}
调用calculateInSampleSize计算压缩比。并解码原图为Bitmap:
/**
* @param imagePath
* @param displayWidth
* @param displayHeight
* @return
*/
private Bitmap compress(String imagePath, int displayWidth, int displayHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;//只测量image 不加载到内存。
BitmapFactory.decodeFile(imagePath, options);//测量image
options.inPreferredConfig = Bitmap.Config.RGB_565;//设置565编码格式,省内存,
options.inSampleSize = calculateInSampleSize(options, displayWidth, displayHeight);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeFile(imagePath, options);//按照Options配置去加载图片到内存
return bitmap;
}
这里比较重要的inJustDecodeBounds字段,当inJustDecodeBounds为true时,调用BitmapFactory.decode时并没有把图片加载到内存中去,只是去测量图片的宽高,不不占用内存,当inSampleSize为false时,调用BitmapFactory.decoe时就把图片加载到内存去了,所有获取bitmap应在inJustDecodeBounds为false后的BitmapFactory.decode去获取。
设置编码格式
android中默认格式是ARG8888,我们解码图片一般用ARG565节省图片加载到内存的大小。
ALPHA_8 代表8位Alpha位图
ARGB_4444 代表16位ARGB位图
ARGB_8888 代表32位ARGB位图
RGB_565 代表8位RGB位图
位图位数越高代表其可以存储的颜色信息越多,当然图像也就越逼真
图片质量压缩
ByteArrayOutputStream out = new ByteArrayOutputStream();//字节流输出
bitmap.compress(Bitmap.CompressFormat.JPEG,50,out);//压缩成jpeg格式
compress中第一个参数是输出文件的格式,在Bitmap枚举类CompressFormat中定义,有JPEG,PNG
PNG,WEBP,一般选择JPEG,压缩出来的容量小,WEBP很耗时,
耗时时间比较:WEBP>PNG>JPEG,
压缩大小:PNG>WEBP>JPEG.
第二个参数是压缩的质量比例,也就是压缩像素的显示色彩,当100时表示不压缩。当为50时表示压缩50%的质量。设置这个参数可以有效的极大的缩小图片的大小,可以按照自己的需求进行设置,
但建议一般不要大于60.第三个参数就是想要写入的图片数据的字节流数组了。
字节流写出文件
我们经过上述步骤后,就拿到了字节流数据了,此时我们可以根据项目需求直接上传字节流或者保存为本地图片再上传。
ByteArrayOutputStream out = new ByteArrayOutputStream();//字节流输出
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, out);//压缩成jpeg格式 压缩像素质量为50%
String fileName = imagePath.substring(imagePath.lastIndexOf("/") + 1, imagePath.lastIndexOf("."));//获取文件名
File outFile = new File("/storage/emulated/0/photoPickTemp", fileName + "_temp.jpeg");//创建压缩后的image文件
try {
if (!outFile.exists()) {//判断文件是否存在
if (outFile.createNewFile()) {//判断创建新文件是否成功
FileOutputStream fos = new FileOutputStream(outFile);
byte[] bytes = out.toByteArray();//字节数组
int count = bytes.length;
fos.write(bytes, 0, count);
fos.close();//关闭流
out.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
获取当前屏幕宽高
工具类
封装
public class ImageUtil {
public ImageUtil(){
}
public static File compressImage(String imagePath,int displayWidth,int displayHeight){
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;//只测量image 不加载到内存
BitmapFactory.decodeFile(imagePath,options);//测量image
options.inPreferredConfig= Bitmap.Config.RGB_565;//设置565编码格式 省内存
options.inSampleSize=calculateInSampleSize(options,displayWidth,displayHeight);//获取压缩比 根据当前屏幕宽高去压缩图片
options.inJustDecodeBounds=false;
Bitmap bitmap=BitmapFactory.decodeFile(imagePath,options);//按照Options配置去加载图片到内存
ByteArrayOutputStream out=new ByteArrayOutputStream();//字节流输出
bitmap.compress(Bitmap.CompressFormat.JPEG,50,out);//压缩成JPEG格式 压缩像素质量为50%
String fileName=imagePath.substring(imagePath.lastIndexOf("/")+1,imagePath.lastIndexOf("."));//获取文件名称
File outFile=new File("/storage/emulated/0/PhotoPickTemp",fileName+"_temp.jpeg");//创建压缩后的image文件
try {
if(!outFile.exists()){//判断新文件是否存在
if(outFile.createNewFile()){//判断创建新文件是否成功
FileOutputStream fos=new FileOutputStream(outFile);//创建一个文件输出流
byte[] bytes=out.toByteArray();//字节数组
int count=bytes.length;//字节数组的长度
fos.write(bytes,0,count);//写到文件中
fos.close();//关闭流
out.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
return outFile;
}
public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){//计算图片的压缩比
final int height=options.outHeight;//图片的高度
final int width=options.outWidth;//图片的宽度 单位1px 即像素点
int inSampleSize=1;//压缩比
if(height>reqHeight||width>reqWidth){
final int halfHeight=height/2;
final int halfWidth=width/2;
while ((halfHeight/inSampleSize)>=reqHeight
&&(halfWidth/inSampleSize)>=reqWidth){
inSampleSize*=2;
}
}
return inSampleSize;
}
}
Android-性能优化
布局优化
- 布局嵌套过深
ConstraintLayout 约束布局
- 使用合适的布局
三种常见的ViewGroup的绘制速度:
FragmentLayout > LinearLayout >RelativeLayout
- 列表控件优化
convertView复用,ViewHolder的使用避免重复遍历节点
- 使用include标签
<merge>标签可以和<include>标签一起使用从而减少布局层级
- ViewStub延时加载
ViewStub本身没有宽高,加载起来几乎不消耗什么资源,当对他setVisibility(View.VISIBLE)的时候会调用他的引用的真实布局填充到当前位置,从而实现了延时加载,节省了正常加载的速度。
- 移除Activity默认背景
主要我们不需要Activity的默认背景,就可以移除掉,减少Activity的渲染时间,提升启动效率。
线程优化
ViewPager,ScrollView-嵌套ViewPager滑动冲突解决
Android事件分发机制
- 对于dispathTouchEvent,onTouchEvent,return true 是终结事件传递。return false 是回溯到父View的onTouchEvent方法。
- ViewGroup想把自己分发给自己的onTouchEvent,需要拦截器onInterecptTouchEvent方法return true把事件拦截下来。
- ViewGroup的拦截器默认是不拦截的。所以super.onInterecptTouchEvent()= return true;
- View没有拦截器,为了让View可以把事件分发给自己的onTouchEvent,View的dispatchTouchEvent默认实现(super)就是把事件分发给自己的onTouchEvent
ViewGroup和View的dispatchTouchEvent是做事件分发,那么这个事件分发可能分发出去的四个目标。
java锁的种类以及辨析
自旋锁
采用让当前线程不停地在循环体内执行实现的。当循环条件被其他线程改变时,才进入临界区
阻塞锁(synchronized)
可重入锁(递归锁)
指的是同一线程外层函数获得锁之后,内层递归函数仍然有获取该锁的代码,但不受影响。
可重入锁最大的作用是避免死锁。
互斥锁的优缺点:
- 优点:能有效防止因多线程抢夺资源造成数据安全的问题
- 缺点:需要消耗大量的CPU资源。
互斥锁的使用前提:多条线程抢夺同一块资源。
** 自旋锁更适合做一些较短的操作 **
ANR
ANR
应用程序无响应。
为什么会产生ANR
5s内无法响应用户输入事件(例如键盘输入,触摸屏幕等)
BroadcastReceiver在10s内无法结束
造成以上两种情况的首要原因就是在主线程(UI线程)里面做了太多的阻塞耗时操作,例如:文件读写,数据库读写,网络查询。
如何避免ANR
不要在主线程里面做繁重的操作
ANR的处理
- 主线程堵塞的
开辟单独的子线程来处理耗时阻塞事物。
- CPU满负荷,I/O阻塞的
I/O阻塞一般就是文件读写或者数据库操作执行在主线程了,也可以通过开辟子线程的方式异步执行。
- 内存不够用的
增大VM内存,使用largeHeap属性,排查内存泄露
RxJava
给 Android 开发者的 RxJava 详解-flatMap():
2018年3月8日
Android-安装文件(APK)瘦身
- 掌握良好的编码习惯
- 使用混淆(Proguard)
- 广泛使用Lint(代码检查)
- 对资源文件进行取舍(没必要适配低端机或非主流机型)
- 资源文件最少化配置(重用,去除不需要的库)
- 压缩图片(.9图)
- 限制app支持的cpu架构的数目(armabi 和 x86 架构就够了)
- 在合适的时候使用代码渲染图像
2018年3月9日
Android应用程序启动过程源代码分析
Step 1. Launcher.startActivitySafely
在Android系统中,应用程序是由launch启动起来的,其实,launch本身也是一个应用程序,其他的应用程序安装后,就会launcher的界面上出现一个相应的图标。点击这个图标,launcher就会对应的应用程序启动起来。
Android2017-2018最新面试题(3-5年经验个人面试经历)
大图加载控件
2018年3月12日
Android插件化与热修复
DynamicApk-携程
- 支持类的加载
- 用代理的方式加载Activity
- 反射方式加载资源
没有像DroidPlugin那样是资源独立的,而是采用同一套资源,用资源名称去区分不同的资源,针对资源的id也做修改,Activity的加载方式是采用代理的方式让系统识别的。
DroidPlugin-360手机助手
原理:利用Android一个进程可以运行多个apk的机制,通过API欺骗让系统认为只有宿主app存在,同时通过预先占坑来创造插件app的运行环境,最后通过动态代理实现函数hook,Binder代理绕过部分系统服务限制,从而实现应用的组件化。
- DroidPlugin这个是跟360安全的产品密切相关的,也非常适合360手机助手。
DroidPlugin的目标是使任何一个app都可以在DroidPlugin的宿主应用中直接运行。而不是将app安装到手机中,再去体验app。 - DroidPlugin中使用了大量的hook相关技术,特别是在Activity的注册上,做了预先注册一些Activity,然后在新的Activity启动时欺骗系统是预先注册过的Activity。
DroidPlugin最精华的就是hook技术。
HotFix-QQ空间热补丁技术
主要技术是:系统在安装app的时候会在类加载器中加载apk中的默认classes.dex文件。这个类加载器可以加载后续的补丁Dex文件,而且可以调整Dex文件的加载顺序,这样就可以用同名的类去替换掉原始的含有bug的类,已达到修复bug的目的。
AndFix-阿里支付宝
- AndFix的原理是方法的替换,把有bug的方法替换成补丁文件中的方法,在Native层使用指针替换的方式替换bug的方法,已达到修复bug的目的。
Alibaba-AndFix Bug热修复框架原理及源码解析
Tinker-微信
在客户端将补丁文件和app原始文件进行合并,用客户端的存储空间换取网络传输的流量和app的运行效率。Tinker目前开源支持的比较好,网上资料相对多一些,在编译时通过新旧两个Dex文件生成差异的patch.dex文件,在app中将差异的patch.dex文件重新跟原始安装包的原始的Dex文件生成新的Dex文件。这个过程会耗费app的运行时间和内存,所以Tinker将其单独放在一个后台进程:patch中。为了将补丁包尽量的小,Tinker自研了DexDiff算法,他深度利用Dex的格式来减少差异文件的大小。
2018年3月13日
Java基础
java中==和equals和hashCode的区别
==
在用关系操作符 == 比较的是值本身,
int n=3;
int m=3;
System.out.println(n==m);
String str = new String("hello");
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1==str2);
str1 = str;
str2 = str;
System.out.println(str1==str2);
结果是:true,false,true
对象类型的比较,比较的是地址(引用),而非值本身,也是就是说他们实际存储的内存地址不同。
equals
比较两个对象的引用是否相等,即 是否指向同一个对象。
总结
对于==,如果作用于基本数据类型,则直接比较其存储的“值”是否相等,如果作用于引用类型的变量,则比较的是所指向的对象的地址。
对于 equals 方法,注意:equals不能作用于基本数据类型,如果没有对equals进行重写,则比较的是 引用类所指向的地址。如果重写了,比较的就是对象的内容。
hashCode
用来鉴定两个对象是否相等,Object类中的hashCode方法返回对象在内存中地址转换成的一个int值,所以如果没有重写hashCode方法,任何对象的hashCode方法是不相等的。
设计hashCode()时最重要的因素就是:无论何时,对同一个对象调用hashCode都应该产生同一个值。
如果重写了equals方法就必须要重写hashCode方法,以便用户将对象插入到散列表中。
equals相等的两个对象,hashCode一定相等,equals不相等的两个对象,却并不能证明他们的hashCode不相等。
equals方法不相等的两个对象,hashCode有可能相等,
在每个覆盖了equals方法的类中,也必须覆盖hashCode方法,如果不这样做的话,就会违反Object.hashCode的通用约定。从而导致该类无法结合所有基于散列的集合一起正常运作。
int、char、long 各占多少字节数
byte 是 字节
bit 是 位
1 byte = 8 bit
- char在java中是2个字节,java采用unicode,2个字节来表示一个字符
- short 2个字节
- int 4个字节
- long 8个字节
- float 4个字节
- double 8个字节
int 与 integer 区别
- Integer是int的包装类,int则是java的一种基本数据类型。
- Integer变量必须实例化后才能使用,而int变量不需要。
- Integer是对象的引用,当new一个Integer时,实际上生成一个指针指向此对象,而int则是直接存储数据值。
- Integer默认值是null,int的默认值是0。
记录-
2018年3月14日
String、StringBuffer、StringBuilder区别
- String :字符串常量(线程安全)
字符串是不变的,他们的值在创造后就不能改变。
字符串缓冲区支持可变字符串。因为字符串对象是不可变的,所以他们可以共享。总结归纳了String的两个最重要的特点:
- String是值不可变的常量,是线程安全的
- String类使用了final修饰符,String类是不可继承的。
StringBuilder:字符串变量(非线程安全)
StringBuffer:字符串变量(线程安全)
是一个容器,最终会通过toString方法变成字符串。
String与StringBuffer区别
- 主要区别:在修改时对象自身是否可变
String在修改时不会改变对象自身。每次对String类型进行改变的时候其实都等同于生成了一个新的String对象,然后将指针指向了新的String对象,所以经常改变内容的字符串最好不要用String。
StringBuffer在修改时会改变对象自身。每次结果都会对StringBuffer对象本身进行操作,而不是生成新的对象,改改变对象引用,所以一般情况下我们推荐使用StringBuffer,特别是字符串对象经常改变的情况下,StringBuffer的主要操作是append和insert方法
- StringBuffer对象和String对象之间的互转:
String s = “abc”;
StringBuffer sb1 = new StringBuffer(“123”);
StringBuffer sb2 = new StringBuffer(s); //String转换为StringBuffer
String s1 = sb1.toString(); //StringBuffer转换为String
总结
- 如果要操作少量的数据用String
- (多线程下)经常需要对一个字符串进行修改,例如追加、插入和删除等操作,使用StringBuffer更加适合一些。
StringBuffer与StringBuilder区别
- StringBuilder是可变的对象,是5.0新增的
此类提供一个与StringBuffer兼容的API,但不保证同步,该类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍) - 线程安全性:
- StringBuffer线程安全的
- StringBuilder线程非安全
- String、StringBuilder、StringBuffer速度区别
- 大多数情况下:StringBuffer > String
由于String对象不可变,重复新建对象,StringBuffer对象可变。 - StringBuilder > StringBuffer
当我们在字符串缓冲区被多个线程使用时,JVM不能保证StringBuilder的操作是安全的,虽然他速度快,但是可以保证StringBuffer是可以正确操作的,当然大多数情况下就是我们是在单线程下进行的操作,使用大多数情况下建议用StringBuilder而不是用StringBuffer。
- 特殊情况, String > StringBuffer
//String效率是远要比StringBuffer快的:
String S1 = “This is only a” + “ simple” + “ test”;
StringBuffer Sb = new StringBuilder(“This is only a”).append(“simple”).append(“ test”);
//String速度是非常慢的:
String S2 = “This is only a”;
String S3 = “ simple”;
String S4 = “ test”;
String S1 = S2 +S3 + S4;
总结
- 如果要操作少量的数据用String
- 多线程操作字符串缓冲区下操作大量数据用StringBuffer;
- 单线程操作字符串缓冲区下操作大量数据用StringBuilder;
对java多态的理解
- 什么是多态
- 面向对象的三大特性:继承,封装,多态,从一定角度来看,封装和继承几乎都是为多态而准备的,
- 多态的定义:
指允许不同类的对象对同一消息做出响应,即同一消息可以根据发送的对象的不同而采用多种不同的行为。 (发送消息就是函数调用)
- 实现多态的技术称为:
动态绑定,是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
- 多态的作用:
消除类型之间的耦合关系。
- 多态存在的三个必要条件:
1.要有继承,2. 要有重写,3.父类引用指向子类对象。
- 多态的好处:
- 可替换性。多态对已存在的代码具有可替换性。
- 可扩展性。增加新的子类不影响已存在类的多态性,继承性,以及其他特性的运行和操作,实际上新增功能更容易获得多态功能。
- 接口性。多态是超类通过方法签名。向子类提供一个共同接口,由子类来完善或者覆盖它而实现的。
- 灵活性。他在应用中提现了灵活多样的操作,提高了使用效率。
- 简化性。多态简化对应用软件的代码编写和修改过程,尤其是在处理大量对象的运算和操作时,这个特点尤为突出和重要。
java中多态的实现方式:接口实现,继承父类进行方法的重写,同一个类中方法重载。
多态的三要素:1.继承。2.重写。3.父类引用指向子类对象
package objectandclass;
class A {
public void show(D obj){
System.out.println("A and D");
}
public void show(A obj){
System.out.println ("A and A");
}
}
class B extends A{
public void show(B obj){
System.out.println("B and B");
}
public void show(A obj){
System.out.println("B and A");
}
}
class C extends B{}
class D extends B{}
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
a1.show(b); //A and A
a1.show(c); //A and A
a1.show(d); //A and D
a2.show(b); //B and A
a2.show(c); //B and A
a2.show(d); //A and D
b.show(b); //B and B
b.show(c); //B and B
b.show(d); //A and D
2018年3月15日
什么是内部类?内部类的作用
定义
就是定义在另外一个类里面的类,与之对应,包含内部类的类被称为外部类。
作用
- 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。
- 内部类的方法可以直接访问外部类的所有数据,,,包括私有的数据。
- 内部类所实现的功能使用外部类都可以实现,只是有时候使用内部类更方便。
public class Outer {
private static int i =1;
private int j =10;
private int k =20;
public static void outer_f1(){
}
public void out_f2(){
}
class Inner{
// 内部类中不允许定义静态变量。
// static int inner_i=199;
int j =100;//内部类中的外部类的实例变量可以共存
int inner_i =1;
void inner_f1(){
Log.e("",i+"");//外部类的变量如果和内部类的变量没有同名的,则可以直接用变量名访问外部类的变量。
Log.e("",j+"");//在内部类中访问内部类自己的变量直接用变量名。
Log.e("",""+this.j);//也可在内部类中用 this.变量名 来访问内部类变量。
//访问外部类中与内部类同名的实例变量可以用 外部类名.this.变量名
Log.e("",""+k);//外部类的变量如果和内部类的变量没有同名的,则可以直接用变量名访问外部类的变量。
outer_f1();
out_f2();
}
}
// 外部类的非静态方法访问成员内部类。
public void outer_f3(){
Inner inner = new Inner();
inner.inner_f1();
}
// 外部类的静态方法访问成员内部类,与外部类外部访问成员内部类一样。
public static void outer_f4(){
Outer outer = new Outer();
Inner inner = outer.new Inner();
inner.inner_f1();
}
public static void main(String[] args){
outer_f4();
}
}
2018年3月16日
抽象类和接口
- 抽象类:对一类事物的抽象
定义: 如果一个类中没有包含足够多的信息来描述一个具体的对象,这样的类就是抽象类。
- 接口:对某一行为的抽象
定义: 接口在java中是一个抽象类型,是抽象方法的集合。一个类通过实现接口的方式,从而继承接口中的抽象方法。
抽象类的意义:
- 为子类提供一个公告的类型;
- 封装子类中的重复内容(成员变量和方法)
- 定义有抽象的方法,子类虽然有不同的实现,但该方法的定义是一致的。
区别总结:
- 抽象类只能单继承,接口能多实现。
- 抽象类是一个类,可以被任意权限修饰符修饰,静态和非静态,final和非finl属性。可以有抽象方法和非抽象方法;接口只能被public final 修饰,只能有静态方法,即使没有显示的声明,而且是不能修改的。
- 抽象的事物不同:
抽象类是对事物的抽象,接口是对行为的抽象;抽象类是对整个类的抽象,包括行为,属性;接口是对类的行为(局部)进行抽象。
- 定义的时候,定义抽象类和接口的思想不同:
设计抽象类是自下而上的过程,我子类需要,所以我定义抽象类;设计接口是自上而下的过程,我接口规范某一行为,我某类需要这个行为,我某类实现某接口。
- 抽象类可以有构造器,而接口不能有构造器。
核心区别:
调用者使用的动机不同,实现接口 为了使用其他规范的某一个行为;
继承抽象类是为了使用这个类的属性和行为
总结
- 抽象类和接口都不能直接实例化,若要实例化,抽象类变量必须指向实现所有的抽象方法的子类对象。接口变量必须指向实现所有接口方法的类对象。
- 抽象类要被子类继承,接口要被类实现。
- 接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现。
- 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
- 抽象类中抽象方法必须全部被子类实现,如果子类不能全部实现父类抽象方法,那么 该子类只能是抽象类,同样,一个实现接口的时候,如不能全部实现接口方法,那 么该类也只能是抽象类。
- 抽象方法只能申明,不能实现,接口是设计的结果,抽象类是重构的结果。
- 抽象类里可以没有抽象方法。
- 如果一个类里有抽象方法,那么这个类一定是抽象类。
- 抽象方法要被实现,所以不能是静态的。也不能是私有的。
- 接口可继承接口,并可多继承接口,但类只能单继承。
- 抽象类主要用来抽象类别,接口主要用来抽象功能。
- 抽象类中,且不包含任何实现,派生类必须覆盖他们,接口中的所有方法都必须是未实现的。
使用场景
再设计类时,如果有些方法我们能确定,而有些方法不能确定,这时候就可以定义成抽象类。抽象类的应用场景非常多,例如模板方法模式就是抽象类的一个应用,JDK中inputStream和outputStream也是抽象类的一个应用,这两个类定义了如何读写数据的方法,而没有定义从哪里去读,具体从哪里读是由具体的实现子类确定。
我们在定义相互调用规则时,可以使用接口,面向接口进行编程的好处,就是能极大地降低软件系统的耦合性,接口的定义按照接口进行调用,而实现者去实现接口。
在JDK中存在很多针对接口的编程,例如用于我们比较两个对象的Comparable接口就是一个典型的案例,我们在自定义对象时,如果实现了该接口,那么我们把对象保存到treeset集合中,treeset将针对接口调用对象的compareTo方法。