精华内容
下载资源
问答
  • 背景项目中遇到需要监控 System.loadLibrary 方法调用,实现如果链接失败则弹出对话框的「问题」。「解决方案」就是 hook java.lang.Runtime#nativeLoad 方法对应的 JNI 函数入口 art...这个需要在运行时拿到一个 Ja...

    背景

    项目中遇到需要监控 System.loadLibrary 方法调用,实现如果链接失败则弹出对话框的「问题」。「解决方案」就是 hook java.lang.Runtime#nativeLoad 方法对应的 JNI 函数入口 art#ArtMethod#ptr_sized_fields#data_ 字段,从而先跳转到我们 hook 的函数再执行系统的 JNI 函数。这个需要在运行时拿到一个 Java 方法的相关信息,比如 Method 类、ArtMethod 类、JNI 函数信息等。反射是能读写一个类、对象的内部数据的一种手段,在此做记录。

    问题

    我们能不能拿到系统某些 private 字段的信息 ?

    我们能不能执行系统某些 private 方法?

    我们能不能拦截某些系统的行为?

    接下来记录一下使用反射拦截主线程消息队列例子

    反射的使用方法

    拿到 Class 对象

    通过 java.lang.Class#forName(java.lang.String) 拿类名对应的类

    通过 java.lang.Object#getClass 拿对象所属的类

    通过 java.lang.Class#getSuperclass 拿父类

    拿到 Field / Method 对象

    拿当前类(不包含父类)字段 java.lang.Class#getDeclaredField

    拿当前类方法 java.lang.Class#getDeclaredMethod

    读写字段 / 执行方法

    读字段 java.lang.reflect.Field#get

    写字段 java.lang.reflect.Field#set

    执行方法 java.lang.reflect.Method#invoke

    拦截主线程消息处理

    2ad0aa4152f4887376bad89a85efde40.png

    Android Message 处理流程

    Android 主线程是设计成事件驱动,Java 层的核心是生产者/消费者模型

    主线程属于消费者,负责从 android.os.MessageQueue 中取消息执行

    当前进程内的所有线程都可以作为生产者向 android.os.MessageQueue 中投递消息,这样就让某些代码在主线程执行

    主线程执行到 android.os.Looper#loop 中时就进入了轮询过程,取消息,执行消息

    由于 android.os.Looper#sThreadLocal 变量是 java.lang.ThreadLocal 类型,所以每个 java.lang.Thread 对象至多有一个 android.os.Looper 对象

    因此我们需要拿到主线程的 android.os.MessageQueue 和 android.os.Looper 对象

    利用 android.os.Looper#getMainLooper 接口可以很简单拿到 Looper 对象

    拿到 Looper 对象后可以利用反射遍历对象的所有字段,拿到 android.os.Looper#mQueue 字段

    接下来我们模拟 android.os.Looper#loop 内部的实现,反射执行 android.os.MessageQueue#next 方法取得一条消息

    找到消息所属的 Handler 对象 android.os.Message#getTarget

    给 Handler 派发消息 android.os.Handler#dispatchMessage

    这样我们在应用层就能够看到主线程的每条消息,从而改变系统的某些行为,比如某些消息不执行

    // 拿到 Looper 对象

    Looper mainLooper = Looper.getMainLooper();

    // 拿到 MessageQueue 对象

    MessageQueue queue = (MessageQueue) Reflection.field(null, null, mainLooper, "mQueue");

    while (true){

    // 取消息

    Message msg = (Message) Reflection.call(null, null, queue, "next", new Class[]{});

    if (msg != null){

    Log.d(TAG, "run: " + msg);

    // 这里拦截消息

    ...

    Handler handler = msg.getTarget();

    // 派发消息

    handler.dispatchMessage(msg);

    }

    }

    练习题

    利用反射遍历类层次结构

    // 拿到 Class 对象

    Class c = xx;

    while (c != null) {

    // 读取 c 信息

    ...

    // 指向父类

    c = c.getSuperclass();

    }

    利用反射遍历对象的所有字段和值

    Class c = xx;

    while (c != null) {

    Field[] fields = c.getDeclaredFields();

    for (Field f : fields) {

    try {

    f.setAccessible(true);

    Log.d(TAG, f + " = " + f.get(obj));

    } catch (Exception e) {

    e.printStackTrace();

    }

    }

    c = c.getSuperclass();

    }

    利用反射修改 private 字段和执行 private 方法

    Class c = xx;

    // 遍历类结构

    while (c != null) {

    // 遍历当前类所有字段

    for (Field f : c.getDeclaredFields()) {

    // 查找指定字段名

    if (f.getName().equals(fieldName)) {

    f.setAccessible(true);

    // 修改字段的值

    f.set(o, value);

    return;

    }

    }

    c = c.getSuperclass();

    }

    Class c = xx;

    while (c != null) {

    Method m = null;

    try{

    // 在当前类查找

    m = c.getDeclaredMethod(name, parameterTypes);

    } catch (Throwable e){

    Log.e(TAG, "call: ", e);

    }

    if (m != null){

    m.setAccessible(true);

    // 执行方法

    value = m.invoke(o, args);

    break;

    }

    c = c.getSuperclass();

    }

    总结

    在读写系统私有信息方面,反射是一个不错的技术手段。

    展开全文
  • 前言:在次做项目的过程中要遍历list集合,然后根据条件删除list集合中不需要对象,尝试了list.remove()方法,根本达不到目的,最后在网上看了几帖子后才知道,要想根据条件删除list集合里面的对象,一定要...

    前言:在一次做项目的过程中要遍历list集合,然后根据条件删除list集合中不需要的对象,尝试了list.remove()方法,根本达不到目的,最后在网上看了几个帖子后才知道,要想根据条件删除list集合里面的对象,一定要使用Iterator遍历删除。结合网上的帖子自己总结了一些,记录在博客当中。

    java集合遍历删除的方法:

       1、当然这种情况也是容易解决,实现方式就是讲遍历与移除操作分离,即在遍历的过程中,将需要移除的数据存放在另外一个             集合当中,遍历结束之后,统一移除。

        2、使用Iterator遍历删除。

    使用Iterator遍历删除的原因:

    Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。
    所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。

    今天代码里写了这几行代码:

    看的其他帖子也会出现同样的错误,例如一下代码也会出现NoSuchElementException异常

    正确的写法应该是:it.next()只能出现一次

    展开全文
  • 场景在企业架构管理中使用树形结构进行...一个节点只能有一个父级节点。2.首先根据参数传递的id属性查询到pid为这个id的对象a,然后再获取这个刚查询的对象a的id,再查询pid为对象a的id的对象b,以此类推。Listres...

    场景

    在企业架构管理中使用树形结构进行管理,如图:

    353e962430396dd60f47f79820cb4682.png

    注:如果A的id是B的pid,那么A就是B的父级。

    数据库数据如下:

    8209c8d793b68875a01e75e64cd9ba9b.png

    现在需要根据传递的id查询此节点所有的父级节点以及此节点所有的子级节点。

    实现

    递归查询父级节点

    1.一个节点只能有一个父级节点。

    2.首先根据参数传递的id属性查询到pid为这个id的对象a,然后再获取这个刚查询的对象a的id,再查询pid为对象a的id的对象b,以此类推。

    Listresult1 = new ArrayList();

    public void selectFather(Long id){

    //查询数据库中对应id的实体类

    SysEnterpriseOrg sysEnterpriseOrg = sysEnterpriseOrgMapper.selectById(id);

    //查询父级架构

    //此处使用mybaatisPlus的条件构造器,查询id等于pid的对象

    QueryWrappersysEnterpriseOrgChildQueryWrapper = new QueryWrapper();

    sysEnterpriseOrgChildQueryWrapper.eq("id",sysEnterpriseOrg.getPid());

    //查询出符合条件的对象

    sysEnterpriseOrg= sysEnterpriseOrgMapper.selectOne(sysEnterpriseOrgChildQueryWrapper);

    if(sysEnterpriseOrg!=null){

    //如果查出的对象不为空,则将此对象的id存到全局变量中,并且继续调用自己,即递归,一直到查询不到为止

    result1.add(sysEnterpriseOrg.getId());

    selectFather(sysEnterpriseOrg.getId());

    }

    }

    递归查询子级节点

    1.一个节点可能有多个子级节点,每个自己节点可能还有更多的子级节点。

    2.所以递归时的参数用一个list来接受,首先遍历参数list,分别查询pid为参数id的对象。

    3.每一个参数id所查询返回的数据是一个对象的list。

    4.遍历list获取符合条件的对象的id值,一份存到temp中用作递归的参数,并存到全局变量中用来获取所有符合条件的id。

    Listresult = new ArrayList();

    public void selectChild(Listids){

    //用来存取调用自身递归时的参数

    Listtemp= new ArrayList();

    //查询数据库中对应id的实体类

    ListsysEnterpriseOrgList = new ArrayList();

    //遍历传递过来的参数ids

    for (Long id :ids) {

    //查询子级架构

    //此处使用mybaatisPlus的条件构造器,查询pid等于id的对象

    QueryWrappersysEnterpriseOrgChildQueryWrapper = new QueryWrapper();

    sysEnterpriseOrgChildQueryWrapper.eq("pid",id.toString());

    //查询结果返回一个list

    sysEnterpriseOrgList= sysEnterpriseOrgMapper.selectList(sysEnterpriseOrgChildQueryWrapper);

    //遍历list获取符合条件的对象的id值,一份存到temp中用作递归的参数,并存到全局变量中用来获取所有符合条件的id

    for (SysEnterpriseOrg s:sysEnterpriseOrgList) {

    temp.add(s.getId());

    result.add(s.getId());

    }

    }

    if(temp.size()!=0&&temp!=null){

    selectChild(temp);

    }

    }

    展开全文
  • 现在假设有颗这样树,(是不是二叉树都没关系,原理都是一样的) 1.广度优先遍历 英文缩写为BFS即...广度优先遍历树,需要用到队列(Queue)来存储节点对象,队列的特点就是先进先出。例如,上面这颗树的访问如

    现在假设有一颗这样树,(是不是二叉树都没关系,原理都是一样的)
    在这里插入图片描述

    1.广度优先遍历

    英文缩写为BFS即Breadth FirstSearch。其过程检验来说是对每一层节点依次访问,访问完一层进入下一层,而且每个节点只能访问一次。对于上面的例子来说,广度优先遍历的 结果是:A,B,C,D,E,F,G,H,I(假设每层节点从左到右访问)。

    先往队列中插入左节点,再插右节点,这样出队就是先左节点后右节点了。

    广度优先遍历树,需要用到队列(Queue)来存储节点对象,队列的特点就是先进先出。例如,上面这颗树的访问如下:

    首先将A节点插入队列中,队列中有元素(A);

    将A节点弹出,同时将A节点的左、右节点依次插入队列,B在队首,C在队尾,(B,C),此时得到A节点;

    继续弹出队首元素,即弹出B,并将B的左、右节点插入队列,C在队首,E在队尾(C,D,E),此时得到B节点;

    继续弹出,即弹出C,并将C节点的左、中、右节点依次插入队列,(D,E,F,G,H),此时得到C节点;

    将D弹出,此时D没有子节点,队列中元素为(E,F,G,H),得到D节点;

    以此类推…

    代码:这里以二叉树为例,遍历所有节点的值

    /**
    public class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;
    
        public TreeNode(int val) {
            this.val = val;
    
        }
    
    }
    */
    
    public class Solution {
        public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
            ArrayList<Integer> lists=new ArrayList<Integer>();
            if(root==null)
                return lists;
            Queue<TreeNode> queue=new LinkedList<TreeNode>();
            queue.offer(root);
            while(!queue.isEmpty()){
                TreeNode tree=queue.poll();
                if(tree.left!=null)
                    queue.offer(tree.left);
                if(tree.right!=null)
                    queue.offer(tree.right);
                lists.add(tree.val);
            }
            return lists;
        }
    }
    

    2、深度优先

    英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次。对于上面的例子来说深度优先遍历的结果就是:A,B,D,E,I,C,F,G,H.(假设先走子节点的的左侧)。

    深度优先遍历各个节点,需要使用到栈(Stack)这种数据结构。stack的特点是是先进后出。整个遍历过程如下:

    先往栈中压入右节点,再压左节点,这样出栈就是先左节点后右节点了。

    首先将A节点压入栈中,stack(A);

    将A节点弹出,同时将A的子节点C,B压入栈中,此时B在栈的顶部,stack(B,C);

    将B节点弹出,同时将B的子节点E,D压入栈中,此时D在栈的顶部,stack(D,E,C);

    将D节点弹出,没有子节点压入,此时E在栈的顶部,stack(E,C);

    将E节点弹出,同时将E的子节点I压入,stack(I,C);

    …依次往下,最终遍历完成。

    代码:也是以二叉树为例。非递归

    /**
    public class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;
    
        public TreeNode(int val) {
            this.val = val;
    
        }
    
    }
    */
    
    public class Solution {
        public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
            ArrayList<Integer> lists=new ArrayList<Integer>();
            if(root==null)
                return lists;
            Stack<TreeNode> stack=new Stack<TreeNode>();
            stack.push(root);
            while(!stack.isEmpty()){
                TreeNode tree=stack.pop();      //先往栈中压入右节点,再压左节点,这样出栈就是先左节点后右节点了。
                if(tree.right!=null)
                    stack.push(tree.right);
                if(tree.left!=null)
                    stack.push(tree.left);
                lists.add(tree.val);
            }
            return lists;
        }
    }
    

    深度优先的递归实现:

    public void depthOrderTraversalWithRecursive()  
        {  
            depthTraversal(root);  
        }  
          
        private void depthTraversal(TreeNode tn)  
        {  
            if (tn!=null)   
            {  
                System.out.print(tn.value+"  ");  
                depthTraversal(tn.left);  
                depthTraversal(tn.right);  
            }         
        }
    
    展开全文
  • 4 使用内部类实现迭代器在迭代器模式结构图中,我们可以看到具体迭代器类和具体聚合类之间存在双重关系,其中一个关系为关联关系,在具体迭代器中需要维持一个对具体聚合对象的引用,该关联关系的目的是访问存储在...
  • Java 集合遍历探索

    2019-01-30 09:47:15
    Java集合遍历探索 ...迭代器是种模式,它能够使序列类型的数据结构的遍历行为与被便利的对象本省分离,即当我们遍历时不需要关心数据结构的具体底层结构。只要拿到这个对象,使用迭代器便可以对这个对象...
  • 步骤就是原数据:数组节点化数据:定义 Node节点对象存储节点对象:通过LinkedList保存Node节点对象在操作过程中我们需要将当前结点和前节点、后一节点进行关系绑定package tree;import java.util.LinkedList;...
  • 创建一个存储学生对象的集合,存储3个学生对象 使用程序实现在控制台遍历该集合学生的姓名和年龄来自于键盘录入 思路: 定义学生类, 为了键盘录入数据方便, 把学生类中的成员变量都定义为String类型 创建集合对象...
  • |--需求说明|--实现思路1、使用map.containsKey()判断输入的姓名在不在map里面,如果在就打印2、遍历全班姓名和成绩,需要创建Map.entry,然后在map.entry里面遍历|--代码内容1 public class Students {2 public ...
  • java集合遍历删除的方法:1、当然这种情况也是容易解决,实现方式就是讲遍历与移除操作分离,即在遍历的过程中,将需要移除的数据存放在另外一个集合当中,遍历结束之后,统一移除。2、使用Iterator遍历删除。使用...
  • 二叉树在java中我们使用数组的形式保存原数据,这数组作为二叉树的数据来源,后续对数组中的数据进行节点化操作。 步骤就是原数据:数组 节点化数据:定义 Node节点对象 存储节点对象:通过LinkedList保存Node...
  • 一、采用存储结构  1、顺序存储:采用数组,顺序存储适配于完全二叉树,对于非完全二叉树并不合适,主要体现在空间上的浪费,所以我们需要用到...//自己建一个Node类,树有Node对象组成 private class Node{ pri...
  • 一、新建一个数组 // 初始化数组 int[] array = {5,4,5,9}; 二、遍历数组 System.out.print("{"); for(int i = 0;i<array.length;i++) { if(i != array.length-1) { System.out.print(array[i]);
  • 工作上遇到一个要求两个不同list对象合并后并排序1、问题描述从数据库中查询两张表的当天数据,并对这两张表的数据,进行合并,然后根据时间排序。2、思路从数据库中查询到的数据放到各自list中,先遍历两个list,存...
  • 1、当然这种情况也是容易解决,实现方式就是讲遍历与移除操作分离,即在遍历的过程中,将需要移除的数据存放在另外一个集合当中,遍历结束之后,统一移除。 2、使用Iterator遍历删除。 使用Iterator遍历删除的...
  • 1、Iterator迭代器用来遍历Colletioon接口实现的集合,返回Iterator接口的实例。...②迭代器模式:提供一种方法访问一个容器(container)对象中各个元素,而不需要暴露该对象的内部细节。迭代器模式,可以说是专门为...
  • 用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。 迭代器模式属于行为型模式。 示例: Iterator.java package com.test.dp.iterator; //迭代器实现的接口 public interface Iterator { ...
  • 初识JAVA:二叉树之中序遍历

    千次阅读 2019-03-28 18:08:25
    二叉树的基本原理:取第一个元素作为根节点,之后每一个元素的排列要求:如果比根节点小的数据放在左子树,如果比根节点大的数据放在右子树,在输出的时候采用中序遍历(左-根-右)的方式完成。 但是,不管是何种...
  • 优点: 1、它支持以不同的方式遍历一个集合对象。 2、迭代器简化了集合类。 3、对同一个集合上可以有多个遍历。 4、在迭代器模式中,增加新的集合类和迭代器类都很方便,无须修改原有代码。 缺点:由于迭代器模式...
  • 这篇文章主要讲什么Hashtable及其内部类的部分源码分析Hashtable在遍历时的Java.util.ConcurrentModificationException异常的来由和解决单机在内存中缓存数据并定期清除过期缓存的简单实现事情的起因工作中需要在...
  • 这篇博客主要讲什么事情的起因工作中需要在某个业务类中设置一个将一些对象缓存在内存中的一个缓存机制(单机)。于是有了以下类似结构的实现:1 packageorg.cnblog.test;23 importjava.util.Hashtable;4 importjava....
  • JAVA是面向对象的语言,开发者在操作数据的时候,通常更习惯面对一个特定类型的对象,如一个用户就是一个User类的对象。DAO层需要做的,就是为上层提供充分的对象支持,让上层再也看不到具体的数据,而是一个个...
  • 一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序。 常用的实现类有 ArrayList、LinkedList 和 Vector。ArrayList 最为流行,它提供了使用索引的随意访问,而 LinkedList 则对于经常需要从 List...
  • 需要考虑因素,高效应权衡多方面因素数据量是否会很...01. 先学着实现一个简单的Java版的单项链表构建任意长度的任意数值的链表, 头插法,顺序遍历输出链表package com.szs.list;/*** 单链表* @author Administrator*...
  • package 三种方法遍历容器对于方法,不同的容器底层数据结构不同,需要写的遍历方法就会不同,因此就有了方法二:迭代器Iterator这工具来实现对底层的屏蔽,只要获取了迭代器对象,就能去遍历各种容器了!...
  • 需要考虑因素,高效应权衡多方面因素数据量是否会很...01. 先学着实现一个简单的Java版的单项链表构建任意长度的任意数值的链表, 头插法,顺序遍历输出链表package com.szs.list;/*** 单链表* @author Administrator*...
  • 存储一个班学员信息,假如一个班容纳20名学员,可以用对象数组存储,如果对象数目不确定,用数组储存就有问题。 如果并不知道程序运行时需要多少对象,或者需要更复杂方式存储对象可以使用Java集合框架。 Java集合...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 462
精华内容 184
关键字:

java遍历一个对象需要实现

java 订阅