单例模式
1. 饿汉式 上来就new对象
public class IdGenerator {
private static final IdGenerator instance = new IdGenerator();
private IdGenerator() {}
public static IdGenerator getInstance() {
return instance;
}
}
2. 懒汉式 支持延迟加载(双重检测 支持高并发)
public class IdGenerator {
private static IdGenerator instance;
private IdGenerator() {}
public static IdGenerator getInstance() {
if (instance == null) {
synchronized(IdGenerator.class) { // 此处为类级别的锁
if (instance == null) {
instance = new IdGenerator();
}
}
}
return instance;
}
}
3.静态内部类
SingletonHolder 是一个静态内部类,当外部类 IdGenerator 被加载的时候,并不会创建 SingletonHolder 实例对象。只有当调用 getInstance() 方法时,
SingletonHolder 才会被加载,这个时候才会创建 instance。instance 的唯一性、创建过程的线程安全性,都由 JVM 来保证。所以,这种实现方法既保证了线程安全,又能做到延迟加载
public class IdGenerator {
private IdGenerator() {}
private static class SingletonHolder{
private static final IdGenerator instance = new IdGenerator();
}
public static IdGenerator getInstance() {
return SingletonHolder.instance;
}
}
工厂模式
比如 Java 中的 DateFormat、Calender。String 类的valueOf() 函数
public static String valueOf(char data[], int offset, int count) {
// Android-changed: Replace constructor call with call to new StringFactory class.
// return new String(data, offset, count);
return StringFactory.newStringFromChars(data, offset, count);
}
1.简单工厂 一个工厂类,if-else 创建对象
public class RuleConfigParserFactory {
private static final Map<String, RuleConfigParser> cachedParsers = new HashMap<>();
static {
cachedParsers.put("json", new JsonRuleConfigParser());
cachedParsers.put("xml", new XmlRuleConfigParser());
cachedParsers.put("yaml", new YamlRuleConfigParser());
cachedParsers.put("properties", new PropertiesRuleConfigParser());
}
public static IRuleConfigParser createParser(String configFormat) {
if (configFormat == null || configFormat.isEmpty()) {
return null;//返回null还是IllegalArgumentException全凭你自己说了算
}
IRuleConfigParser parser = cachedParsers.get(configFormat.toLowerCase());
return parser;
}
}
2.工厂方法 单独的工厂类创建相应的对象
public interface IRuleConfigParserFactory {
IRuleConfigParser createParser();
}
public class JsonRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new JsonRuleConfigParser();
}
}
public class XmlRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new XmlRuleConfigParser();
}
}
public class YamlRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new YamlRuleConfigParser();
}
}
public class PropertiesRuleConfigParserFactory implements IRuleConfigParserFactory {
@Override
public IRuleConfigParser createParser() {
return new PropertiesRuleConfigParser();
}
}
3.抽象工厂就是针对这种非常特殊的场景而诞生的。我们可以让一个工厂负责创建多个不同类型的对象(IRuleConfigParser、ISystemConfigParser 等),而不是只创建一种 parser 对象。这样就可以有效地减少工厂类的个数。
public interface IConfigParserFactory {
IRuleConfigParser createRuleParser();
ISystemConfigParser createSystemParser();
//此处可以扩展新的parser类型,比如IBizConfigParser
}
public class JsonConfigParserFactory implements IConfigParserFactory {
@Override
public IRuleConfigParser createRuleParser() {
return new JsonRuleConfigParser();
}
@Override
public ISystemConfigParser createSystemParser() {
return new JsonSystemConfigParser();
}
}
}
// 省略YamlConfigParserFactory和PropertiesConfigParserFactory代码
建造者模式
如dialog、retrofit
1.防止构造函数参数列表太长
2.对参数进行统一校验(如果类的属性之间有一定的依赖关系或者约束条件)
3.如果我们希望创建不可变对象,也就是说,对象在创建好之后,就不能再修改内部的属性值
原型模式
如果对象的创建成本比较大,而同一个类的不同对象之间差别不大(大部分字段都相同),在这种情况下,我们可以利用对已有对象(原型)进行复制(或者叫拷贝)的方式来创建新对象,以达到节省创建时间的目的。这种基于原型来创建对象的方式就叫作原型设计模式(Prototype Design Pattern),简称原型模式。
代理模式
代理模式(Proxy Design Pattern)它在不改变原始类(或叫被代理类)代码的情况下,通过引入代理类来给原始类附加功能。
动态代理的原理与实现静态代理需要针对每个类都创建一个代理类,并且每个代理类中的代码都有点像模板式的“重复”代码,增加了维护成本和开发成本。对于静态代理存在的问题,我们可以通过动态代理来解决。我们不事先为每个原始类编写代理类,而是在运行的时候动态地创建原始类对应的代理类,然后在系统中用代理类替换掉原始类。
桥接模式
一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展。“抽象”和“实现”独立开发,通过对象之间的组合关系,组装在一起。
它非常类似我们之前讲过的“组合优于继承”设计原则,通过组合关系来替代继承关系,避免继承层次的指数级爆炸
举个很简单的例子,现在有两个纬度
Car 车 (奔驰、宝马、奥迪等)
Transmission 档位类型 (自动挡、手动挡、手自一体等)
按照继承的设计模式,Car是一个Abstract基类,假设有M个车品牌,N个档位一共要写M*N个类去描述所有车和档位的结合。
而当我们使用桥接模式的话,我首先new一个具体的Car(如奔驰),再new一个具体的Transmission(比如自动档)。然后奔驰.set(手动档)就可以了。
那么这种模式只有M+N个类就可以描述所有类型,这就是M*N的继承类爆炸简化成了M+N组合。
沙盒路径
Environment.getDataDirectory() = /data
Environment.getDownloadCacheDirectory() = /cache
Environment.getExternalStorageDirectory() = /mnt/sdcard
Environment.getExternalStoragePublicDirectory(“test”) = /mnt/sdcard/test
Environment.getRootDirectory() = /system
getPackageCodePath() = /data/app/com.my.app-1.apk
getPackageResourcePath() = /data/app/com.my.app-1.apk
getCacheDir() = /data/data/com.my.app/cache
getDatabasePath(“test”) = /data/data/com.my.app/databases/test
getDir(“test”, Context.MODE_PRIVATE) = /data/data/com.my.app/app_test
getExternalCacheDir() = /mnt/sdcard/Android/data/com.my.app/cache
getExternalFilesDir(“test”) = /mnt/sdcard/Android/data/com.my.app/files/test
getExternalFilesDir(null) = /mnt/sdcard/Android/data/com.my.app/files
getFilesDir() = /data/data/com.my.app/files
复制文件夹
/**
*
* @param srcPath 当前文件夹路径
* @param toPath 目标文件夹路径
* @return true 成功
*/
public boolean copyDirs(String srcPath,String toPath){
File srcFile = new File(srcPath);
File toFile = new File(toPath);
if (!srcFile.isDirectory() || !toFile.isDirectory()){
return false;
}
for (File file : srcFile.listFiles()) {
if (file.isFile()){
File toFileWithName = new File(toFile, file.getName());
copyFile(file.getAbsolutePath(), toFileWithName.getAbsolutePath());
}else {
copyDirs(file.getAbsolutePath(),toPath);
}
}
return true;
}
/**
*
* @param srcPath 当前文件路径
* @param toPath 目标文件路径
* @return true 成功
*/
public boolean copyFile(String srcPath,String toPath){
File srcFile = new File(srcPath);
File toFile = new File(toPath);
if (!srcFile.exists() || !srcFile.isFile()){
return false;
}
File parentFile = toFile.getParentFile();
if (parentFile != null){
parentFile.mkdirs();
}
try {
FileInputStream fileInputStream = new FileInputStream(srcFile);
FileOutputStream fileOutputStream = new FileOutputStream(toFile);
byte[] buffer = new byte[1024 * 8];
int byteRead;
while (-1 != (byteRead = fileInputStream.read(buffer))) {
fileOutputStream.write(buffer, 0, byteRead);
}
fileInputStream.close();
fileOutputStream.flush();
fileOutputStream.close();
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
dpi 和drawable-xhdpi[Bitmap这个“内存刺客”你也要小心~ (qq.com)
为了简化不同的配置,Android针对不同像素密度范围进行了归纳分组,如下:
android dpi范围.png适用于不同像素密度的配置限定符
我们通常选取中密度 (mdpi) 作为基准密度(1倍图),并保持ldpi~xxxhdpi这六种主要密度之间 「3:4:6:8:12:16」 的缩放比,来放置相应尺寸的图片资源。
例如,在创建Android工程时IDE默认为我们添加的ic_launcher图标,就遵循了这个规则。该图标在中密度 (mdpi)目录下的大小为48x48,在其他各种密度的目录下的大小则分别为:
• 36x36 (0.75x) - 低密度 (ldpi)• 48x48(1.0x 基准)- 中密度 (mdpi)• 72x72 (1.5x) - 高密度 (hdpi)• 96x96 (2.0x) - 超高密度 (xhdpi)• 144x144 (3.0x) - 超超高密度 (xxhdpi)• 192x192 (4.0x) - 超超超高密度 (xxxhdpi)
当我们引用该图标时,系统就会「根据所运行设备屏幕的dpi,与不同密度目录名称中的限定符进行比较,来选取最符合当前设备的图片资源」。如果在该密度目录下没有找到合适的图片资源,系统会有对应的规则查找另外一个可能的匹配资源,并「对其进行相应的缩放,以适配屏幕,由此可能造成图片有明显的模糊失真」。
[图片上传失败...(image-438730-1660274940112)]
不同密度大小的ic_launcher图标 adnroid dpi对应缩放比.png那么,具体的查找规则是怎样的呢?
8Android查找最佳匹配资源的规则
一般来说,Android会「更倾向于缩小较大的原始图像,而非放大较小的原始图像」。在此前提下:
• 假设最接近设备屏幕密度的目录选项为xhdpi,如果图片资源存在,则匹配成功;• 如果不存在,系统就会从更高密度的资源目录下查找,依次为xxhdpi、xxxhdpi;• 如果还不存在,系统就会从「像素密度无关的资源目录nodpi」下查找;• 如果还不存在,系统就会向更低密度的资源目录下查找,依次为hdpi、mdpi、ldpi。
网友评论