基础知识

210 阅读12分钟

面试中问到的知识点

GaoDun

  • Volatile关键字原理:保证线程可见性
  • 单例模式把getInstance()用synchronized修饰,在多线程中获取全局变量还需要加volatile关键字吗?
需要:示例代码如下
//单例
public class Singleton {
    private boolean mFlag = false;//没有加volatile不能保证
	//private volatile boolean mFlag = false;//加volatile保证可见性
    
    private Singleton() {
    }

    private static Singleton sInstance;

    public static synchronized Singleton getsInstance() {
        if (sInstance == null) {
            sInstance = new Singleton();
        }
        return sInstance;
    }

    public void setFlag(boolean flag) {
        this.mFlag = flag;
    }

    public boolean getFlag() {
        return this.mFlag;
    }

}

//测试类
public class TestSingleton {
    public static void main(String[] args) {

        Thread t1 = new Thread(new TRunnable("线程1"));
        Thread t2 = new Thread(new TRunnable("线程2"));
        Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程3结束任务标志位");
                Singleton.getsInstance().setFlag(true);
            }
        });

        t1.start();
        t3.start();
        t2.start();
    }

    static class TRunnable implements Runnable {
        String name;

        public TRunnable(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            System.out.println("--------" + name + "  开始--------");
            while (!Singleton.getsInstance().getFlag()) {
                System.out.println(name + "执行任务。。。");
            }
            System.out.println("--------" + name + "  开始--------");
        }
    }
}
//输出结果:线程3执行了标志位,线程2还会执行任务,单例加锁不能保证可见性
--------线程1  开始--------
--------线程2  开始--------
线程3结束任务标志位
线程2执行任务。。。
--------线程2  开始--------
线程1执行任务。。。
--------线程1  开始--------
//
  • 在单例多线程中是否可以不用给全局变量修饰
  • 认为自己最大的安卓这块的优势
  • 深度没有有,但是要钻研,没有总结,现在所在的项目,都是做好的,没有挑战性。要钻研问题
  • MVVM的使用
  • Rxjava的使用
  • 弱引用的使用,handler中示例,为啥用弱引用
  • 考研经历,怎么上的
  • 怎么实现的账号检测
  • Okhttp的几种常用拦截器
  • Android target作用,会有啥影响
  • 自己做的模块怎么分层设计:model,view,controler这种
  • Kotlin多不多
  • Mvp的各个部分用处
  • 场景题:mvp如果A页面,
  • 网络请求发出去怎么结束掉,能结束掉吗
  • Rxjava中observer怎么和页面绑定,订阅者

DingDongMaiCai

  • Retofite
  • JNI接触过吗
  • Glide
  • Android 9和Android 10适配
  • 图片加载库
  • 网络这块用的
  • Acticity生命周期
  • 如果后台按下去,内存不足,onsaveunstance
  • Livedata相关
  • Jetpack用过吗
  • Okhttp里面
  • SessionCookie用过吗
  • 网络请求有加密吗
  • 职业规划发展,走研发路线还是管理

HONOR

  • 四大组件
  • Activity生命周期
  • 支付的流程
  • 最近项目遇到困难
  • View Touch事件的点击
 public boolean dispatchTouchEvent(MonthinEvent e) {
 	boolean consumed = false;//事件是否被消费
    if(onInterceptTouchEvent(e)) {//如果自己拦截下事件
    	consumed =  onTouchEvent(e);//交给自己处理
    } else {
    	consumed = child.dispatchTouchEvent(e);//交给孩子继续分发
    }
    return consumed;//返回消费结果,ture表示已经消费,false表示交给父的onTouchEvent处理
 }

Shanghai TikTok


  • 类加载的过程 JVM运行时内存分布

  • 创建一个对象的方法有几种

    • new对象
    One one = new One();
    
    • 通过 Class 类的 newInstance() 方法,调用类的默认无参构造函数
    1. Class<One> one= One.class;
    One oneObjcet = one.newInstance();
    
    2. Person p = (Person) Class.forName("com.bean.Person").newInstance();
    
    • 反射Constructor
    Class<?> oneClz = Class.forClass("com.my.One");
    oneClz.newInstance();
    
    • clone()对象
    One one = (One) origin.clone();
    
    • 序列化和反序列化
    序列化
    ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("D:/objectUser.txt"));
    oos.writeObject();
    oos.close();
    
    反序列化
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:/objectUser.txt"));
    User user = (User)ois.readObject();
    
    • 总结示例
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, CloneNotSupportedException {
        //1 new 对象
        Person p1 = new Person();
        //2 newInstance()
        Person p2 = (Person) Class.forName("interview.instance.Person").newInstance();
        //3 Constructor
        Person p3 = (Person) Person.class.getConstructors()[0].newInstance();
        //4 Clone方法
        Person p4 = (Person) p3.clone();
        //5 反序列化
    }
    

  • 为什么要有双亲委派

  • hashCode的作用

  • 如何知道哪个类已经被加载了

  • HandlerThread作用

  • ThreadLocal作用

  • 能不能修改系统加载的类,替换掉如String类

  • sp如何实现加锁的
  • 拦截器解析
  • okhttp实现统计请求时间的拦截器
    • 拦截器分为Appliction拦截器和NetWork拦截器
    • 常用拦截器:RetryAndFollowUpInterceptor, BridgeInterceptor, CacheInterceptor, ConnectInterceptor, CallServerInteceptor
  • Android中有哪些跨进程的实现方式
  • 模块开发中如何实现两个子moudle的通信:桥
  • HashMap的原理
  • HashMap是线程安全的吗
  • CuccreentMap如何实现线程安全的
  • 账号泄露检测中如何保证账号的安全,用什么加密
  • 如果从新设计,如何保证,对称加密实现
  • 从new一个对象开始,如果的过程
  • java中多线程会有什么问题
  • sqlite和sp的区别和为啥用
  • View中的MeasureSpec的size如何确定的
  • sqlite的实现

  • HashTable和HashMap区别
    • HashMap不是线程安全,HashTable是线程安全,方法加了Synchronize
    • HashMap的Key和Value允许null值,HashTable不允许
    • HashMap去掉contains()方法,改为containKey()和containValue(),HashTable是contains()
    • 父类不同,HashTable是继承Dictionary,HashMap是继承AbstractMap实现Map接口
    • 都实现hash算法

  • 什么情况用sqlite
  • sp为什么不能存大的数据
  • 如何遍历一个二叉查找树,并把结果输出成有序

二叉搜索树题目

  • 检查一个树是否是二叉搜索树
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool isValidBST(TreeNode *root) {
        if(!root) return true;
        return (isValidBST(root->left) && isValidVal(root->val) && isValidBST(root->right)); //中序遍历
    }
private:
    bool isValidVal(int val) {
        if(bFirstNode) {
            bFirstNode = false;
            prevNodeVal = val;
            return true;
        }

        if(prevNodeVal >= val) return false;

        prevNodeVal = val;
        return true;
    }

    bool bFirstNode = true;
    int prevNodeVal = 0; //记录前一个结点的值
};
  • 根据递增排序的序列生成二权搜索树
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode *sortedListToBST(ListNode *head) {
        list = head;
        return listToBST(getListLength(head));
    }
private:
    int getListLength(ListNode *node) {
        int length = 0;
        while(node) {
            ++length;
            node = node->next;
        }
        return length;
    }
    TreeNode *listToBST(int n) { //中序遍历
        if(n == 0) return NULL;
        TreeNode *pNode = new TreeNode(0);
        pNode->left = listToBST(n / 2); //左子树共(n / 2)个结点
        pNode->val = list->val;
        list = list->next;
        pNode->right = listToBST(n - n / 2 - 1); //右子树共(n - n / 2 - 1)个结点
        return pNode;
    }

    ListNode *list; //记录当前位置
};

将有序数组转换为二叉搜索树 中序遍历二叉搜索树即可


Bilibili

  • 原子,一致,可见行 *
  • 单例模式几种创建的模式,优缺点
  • http1/http2区别
    • 最大区别是多路复用,2.0请求和响应都是发送是二进制帧,
  • RxJava特点
  • OKHttp的多线程实现网络请求
  • OKHttplanjieq
    • RetryAndFollowUpInterceptor: 做网络连接失败重试
    • BridgeInterceptor:初始化信息,添加头,gzip,keep-alive,返回reponse解压
    • CacheInterceptor:处理缓存操作
    • ConnectionInterceptor:建立链接
    • CallServerInterceptor:真正连接服务器发送并返回服务器数据,http写入数据流,并从io流中读取数据返回客户端
  • 线程池创建的方式,最重要的参数排序
  • 数组组最大的整数
  • 快排,冒泡排序
  • volitate关键字原理
  • sychronized关键字原理
  • 用的Android框架
  • 单例模式
public class Singleton {
	private Singleton() {
    }
    ///volatile防止指令重排
    private volatile static Singleton sIntance;
    
    public static Singleton getInstance() {
    //当多个线程同时调用单例获取方法,先判断当前实例是否创建,已创建直接返回实例
    	if(sIntance == null) {
        //当没有创建实例,如果此时有多个线程:A和B同时抢锁去创建实例,
        //获得锁的线程才能创建
        	synchronized(Singleton.class) {
            //第二步再次判空,因为比如A线程先抢到锁,B线程在外边等待锁,A进入代码块创建实
            //例了,然后A线程释放锁,此时B线程获得锁,加入不再次判断实例是否为空,那么刚
            //A线程创建了实例,B此时进入后又创建了一个实例,不满足单例了,必须在进入同步
            //代码块时候,再次判断实例是否为空,不为空再创建
            	if(sIntance == null) {
                	sIntance = new Singleton();
                }
            }
        }
        return sIntance;
    }
}
* 优点:懒加载,不用时候节约内存空间。
* 缺点:不加锁时候有线程安全问题,可能多个线程多个初始化实例,加锁造成程序串行化,造成同步性能问题。
  • 饿汉模式
public class Singleton {
	private static final Singleton sInstance= new Singleton();
    
    private Singleton(){}
    
    public static getInstance(){
    	return sInstance;
    }
}
* 优点:因为使用了static关键字,保证了调用时候,关于这个变量的所有写操作都加载完成,在jvm层面保证了线程安全。
* 缺点:不能实现懒加载,如果初始化时候就加载了整个类,但是很长时间没有使用,那么造成内存空间的浪费。
  • 静态内部类
public class Singleton {
    //私有构造函数
    private Singleton() {
    }

    private static class InstanceHolder {
        private final static Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return InstanceHolder.instance;
    }
}
* 特点:在没有加锁情况下保证线程安全,并且不会造成空间的浪费
* 优点:可以实现懒加载,加载Singleton类时候并没有加载静态内部类
* 缺点:无法防止反射实例化
  • 枚举单例:枚举类型是线程安全的,并且只会装载一次
public class Singleton {
	private Singleton(){}
    
    private enum SingletonHolder {
    	INSTANCE;
        private final Singleton instance;
        
        SingletonHolder(){
       		instance = new Singleton();
        }
    }
    
    public static Singleton getInstance() {
    	return SingletonHolder.INSTANCE.instance;
    }
}
* 优点:防止反射实例化
* 缺点:无法实现懒加载

TianYanCha

activity与fragment区别

  1. 生命周期区别:Fragment比Activity灵活,因为可以控制的生命周期多
    • Activity生命周期:onCreate()-onStart()-onResume()-onPause()-onStop()-onDestroy() Activity生命周期
    • Fragment生命周期:onAttach()-onCreate()-onCreateView()-onActivityCreate()-onStart()-onResumt()-显示出-onPause()-onStop()-onDestroyView()-onDestroy()-onDetach() Fragment生命周期
  2. 灵活性来说:Fragment更灵活,Fragment可以在xml中定义,也可以在代码里面添加;fragment的替换replace()或者show()或者hide(),切换的时候不会有明显效果,activity的切换会有明显效果影响体验。

自定义view要重写函数

  1. 自定义view详解
  2. 自定义view几种方式:
    • 自定义组合控件,几个控件组合成一个,在多处使用
    • 继承系统View控件,如继承系统TextView并且扩展其功能
    • 继承View控件,重写onMesure() onDraw()方法
    • 继承系统ViewGroup控件,如RelativeLayout并改写里面的方法
    • 继承ViewGroup控件,重写onMeasure(),onLayout(),onDraw()方法
  3. 自定义View | 函数 | 作用 | 相关方法 | |---|---|---| |measure| 测量view宽高|onMeasure(),setMeasuredDimension()| |layout|布局控件|onLayout(),setFrame()| |draw|绘制|onDraw()|

自定义view有几个构造方法 5种构造方法

  1. View()
View() {
	mResources = null;
	mRenderNode = RenderNode.create(getClass().getName(), this);
}
  1. View(Context context)
public View(Context context) {
}
  1. View(Context context, AttributeSet attrs)
public View(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
}
  1. View(Context context, AttributeSet attrs, int defStyleAttr)
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
}
  1. public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)
public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
	final TypedArray a = context.obtainStyledAttributes(
                attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
}

自定义view一般都重写三个构造方法

public StarView(Context context) {
        super(context);
}

public StarView(Context context, @Nullable AttributeSet attrs) {
	super(context, attrs);
}

public StarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
	super(context, attrs, defStyleAttr);
    init(context,attrs);
}

private void init(Context context, AttributeSet attrs) {
        if (attrs == null) {
            return;
        }
        final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.NCRippleViewStyleable);
        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
        mMinRadius = ta.getDimension(R.styleable.NCRippleViewStyleable_nc_ripple_min_radius, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_MIN_RADIUS, displayMetrics));
        mMaxRadius = ta.getDimension(R.styleable.NCRippleViewStyleable_nc_ripple_max_radius, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_MAX_RADIUS, displayMetrics));
        mMinStroke = ta.getFloat(R.styleable.NCRippleViewStyleable_nc_ripple_min_stroke, DEFAULT_MIN_STROKE);
        mMaxStroke = ta.getFloat(R.styleable.NCRippleViewStyleable_nc_ripple_max_stroke, DEFAULT_MAX_STOKE);
        mColor = ta.getColor(R.styleable.NCRippleViewStyleable_nc_ripple_color, DEFAULT_COLOR);
    }

自定义view如果加载xml布局要哪个构造方法

  • 重写两个参数的
public View(Context context, @Nullable AttributeSet attrs) {
	super(context, attrs);
}

View绘制过程

Activity启动和触摸事件传递

  • Activity创建和WindowInputEvnetReciver创建

Activity启动过程

  1. ActivityThread.performLaunchActivity() ->
  2. Activity通过反射方式创建出activity对象:
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
-> AppComponentFactory.instantiateActivity(ClassLoader,className,intent)
-> (Activity) cl.loadClass(className).newInstance()            
  1. Activity创建后执行attach()方法 -> 创建PhoneWindow -> 并把当前Activity作为Callback赋给PhoneWindow
  2. ActivityThread.handleResumeActivity() -> performResumeActivity()获取ActivityClientRecord:包含具体activity对象
  3. ActivityThread.handleResumeActivity() -> Activity.mDecor = Activity.window.getDecorView()将PhoneWindow的decorview赋值给activity的mDecor属性
  4. Activity.window.getDecorView() -> 执行DecorView.installDecor(),创建decorview -> activity中View是 DecorView, activity中mWindow是 PhoneWindow
  5. Activity执行setVisible() -> Activity.makeVisible() -> WindowManager.addView()
void makeVisible() {
        if (!mWindowAdded) {
            ViewManager wm = getWindowManager();
            wm.addView(mDecor, getWindow().getAttributes());
            mWindowAdded = true;
        }
        mDecor.setVisibility(View.VISIBLE);
    }
  1. WindowManager.addView() -> 调用WindowManagerImpl.addView() -> WindowManagerGlobal.addView() -> 创建ViewRootImpl
WindowManagerGlobal.
public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
            ...
            root = new ViewRootImpl(view.getContext(), display);
            view.setLayoutParams(wparams);
            mViews.add(view);//view为decorview
            mRoots.add(root);
            mParams.add(wparams);
            try {
                root.setView(view, wparams, panelParentView);//
            } catch (RuntimeException e) {
               ...
                throw e;
            }
}
  1. ViewRootImpl.setView() -> requestLayout() -> shceduleTraversals() -> doTraversal() -> performTraversals()
  2. 关键performTraversals() -> perfromMeasure() -> performLayout() -> performDraw()
// =====================ViewRootImpl.java=================
private void performTraversals() {
   ......
   int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
   int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
   ......
   // Ask host how big it wants to be
   performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
   ......
   performLayout(lp, mWidth, mHeight);
   ......
   performDraw();
}
  1. View创建和绘制流程图

算法合并两个数组

/**
* m为a数组中元素个数
* n为b数组中元素个数
*/
public void merge(int a[],int b[], int m, init n) {
	int tail = a.length -1;
	int p1 = m -1 ;
    int p2 = n -1;
    while( p1 > 0 && p2 > 0) {
    	if(a[p1] > b[p2]) {
        	a[tail] = a[p1];
            p1--;
        } else {
        	a[tail] = b[p2];
            p2--;
        }
        tail--;
    }
    while(p1 > 0) {
    	a[tail] = a[p1];
        p1--;
    }
    while(p2 > 0) {
    	a[tail] = b[p2];
        p2--;
    }
}

flutter为啥要用redux

randerobject和elment还有widget关系

stateful和stateless widget的区别

stringbuilder和string区别

什么场景用stringbuidler,内存或者实现原理上面说

横竖屏切换时,activity生命周期

  • AndroidManifest.xml不添加配置
onPause
onStop
onDestroy
onCreate
onStart
onRestoreSaveInstance
onResume
  • AndroidManifest.xml添加android:configChanges="orientation|screenSize"
切换横屏:
onConfigChanged()
  • AndroidManifest.xml添加android:configChanges="orientation|screenSize|keyboardHidden"
切换横屏:
onConfigChanged()
切换竖屏:
onConfigurationChanged()
  • 设置Activity的android:configChanges="orientation|keyboardHidden|screenSize"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法
  • 当前Activity和弹窗系统AlertDialog时候,activity不会有生命周期变化
  • 按下Home键:
onPause -> onStop
再切回去:
onRestart -> onStart -> onResume

Anroid基础知识

HashCode的作用

1.hashCode是用来查找的,比如当前系统内存空间:[1][2][3][4][5]五个区域可以存放对象,
如果将对象放在内存中后,想要查找每个对象,比如p1放在内存[3]上,查找的话必须顺序遍历内存地址挨
个查找或者二分法查找。
2.如果使用hashCode,存对象的时候获取对象的hashCode,比如对象有个属性ID,每次存对象的时候将ID
与内存空间5取余:id % 5 = 存放的内存地址,那么查找时候只要直接获取对象hashCode并且直接与空间
取余就可以在O(1)时间内找到对象。
3.但是如果有两个对象存的地址相同,那么就需要用到equals来对比两个对象是否相同。例如:对象1 id为8,
对象2 id为3,对象1存放地址:8 % 5 = 3,对象2存放地址:3 % 5 =3。相当于放在同一个桶里面,去取对象
的时候就需要重写equals来对比俩对象是否相同,找到我们要找的对象。
4.为何重写equals后必须要重写hashCode,因为如果不重写hashCode,就找不到对象存放的那个桶,只写equals
没用
  • 示例: 只重写hashCode时候
public static void main(String[] args) {
        HashSet<TObject1> set = new HashSet<>();
        TObject1 p1 = new TObject1(2);
        TObject1 p2 = new TObject1(2);
        set.add(p1);
        set.add(p2);
        System.out.println(p1.hashCode() == p2.hashCode());
        System.out.println(p1.equals(p2));
        System.out.println(set);
    }

    //测试类1
    static class TObject1 {
        int val;

        public TObject1(int val) {
            this.val = val;
        }

        @Override
        public int hashCode() {
            return Objects.hash(val);
        }
    }
    
结果:
true //两个对象hashCode相同
false //两个对象不是同一个对象,不是内存中同一个对象
[interview.hashcode.HashCodeTest$TObject1@21, interview.hashcode.HashCodeTest$TObject1@21]//hashSet判断

重写hashCode和equals方法

public static void main(String[] args) {
        HashSet<TObject1> set = new HashSet<>();
        TObject1 p1 = new TObject1(2);
        TObject1 p2 = new TObject1(2);
        set.add(p1);
        set.add(p2);
        System.out.println(p1.hashCode() == p2.hashCode());
        System.out.println(p1.equals(p2));
        System.out.println(set);
    }

    //测试类2
    static class TObject1 {
        int val;

        public TObject1(int val) {
            this.val = val;
        }

        @Override
        public boolean equals(Object o) {
            if (o == this) {
                return true;
            }
            if (o == null) {
                return false;
            }
            if (!(o instanceof TObject1)) {
                return false;
            }
            TObject1 to = (TObject1) o;
            if (to.hashCode() == this.hashCode() && to.val == this.val) {
                return true;
            }
            return false;
        }

        @Override
        public int hashCode() {
            return Objects.hash(val);
        }
    }
结果:
true
true
[interview.hashcode.HashCodeTest$TObject1@21]

ArrayList和LinkedList区别

  • 共同点:都是继承List的
  • ArrayList基于数组的,LinkedList基于链表的
  • ArrayList数据都是放在一块内存地址的,而LinkedList是放在不同位置
  • 插入和删除数据是LinkedList更快,只用移动指针即可,而查找数据是ArrayList更快
  • 扩容区别,每次ArrayList都是将数组元素拷贝扩容:实际是System.arrayCopy();
  • LinkedList是双端队列,可以实现栈功能,队列和双端队列功能
  1. ArrayList
  2. LinkedList
  3. 对比总结

ThreadLocal

  • 作用:适用于某些数据以线程为作用域,并且不同线程具有不同数据副本的场景。
  • 内部类ThreadLocalMap
  • 是一个数组
  • 实现线程间数据不扰乱,原理:将线程作为key,存入ThreadLocal时候key-value键值对,所以不同线程不会出现操作同一个entry情况

计算hash方式

  1. 随机数法
  2. 斐波那契散列法
  3. 平方散列法
  4. 除法散列法

voliate的作用

  • 原理:
1. 添加voliate关键字的变量会在编译时候加:ACC_VOLIATE flag修饰
2. 汇编指令:lock addl $0x0,(%rsp),也就是 lock 的前缀指令
3. lock前缀指令,内存屏障:
* 将本处理器缓存刷新到内存中
* 重排序时候不能把后边指令重新排序到内存屏障前面
* 对内存写入动作会导致其他的处理器中对应内存无效
  • 并不能解决原子性问题,需要用到synchronized或者lock
  • synchronized保证原子性,voliate保证可见性
  • 保证内存可见性:每次读写遍历从主内存读取,保证线程间访问的变量及时更新
  • 控制被修饰的变量在内存上的操作主动刷新到主内存中,
  • 禁止指令重排

synchronized同步

Android内存泄露几种情况

  • Handler使用不当导致: 未定义静态内部类,当activity结束时候,还有消息发送到messagequeue队列,此时如果页面销毁,messagequeue中消息未处理完,hanlder还持有activity的引用,此时message和handler被messagequeue持有,导致内存泄露
  • 静态变量导致:静态变量在类加载时候初始化
  • 资源文件,流等使用后未关闭导致的内存泄露
  • 集合类中对象只增不减,不删减会导致
  • 非静态内部类

工厂模式几种写法

  • 原博客解析

  • 简单工厂模式:一个工厂,传入不同的参数type生产对应的对象,对象有公共的父类

  • 工厂模式:不同的工厂buidler,每个工厂生产对应的对象

  • 抽象工厂模式:一个工厂生产多个对应类型的对象,当只生产一个对象时候,就是工厂模式,当生产多个对象时候,是抽象工厂模式

  • 工厂模式是面向一个产品等级,如工厂只要生成鼠标;而抽象工厂是面向多个产品等级:如一个工厂需要生成耳机,键盘,鼠标

工厂模式针对的是一个产品等级结构 ,抽象工厂模式针对的是面向多个产品等级结构的。