逆向爬虫34 Java基础二
目的
- 总结java和之前学过语言之间相似和不同的地方,方便记忆
关于Object ( 上节回顾 )
- Object可以泛指java中的任何对象 —> 父类可以泛指子类实例化的任何对象
常见数据类型
List系列
- List类似python中的列表,可以动态扩容,存放任意数量的数据。
- List是一种接口,其下有两种实现,分别是ArrayList和LinkedList,ArrayList的内存地址是连续的,LinkedList是基于链表形式存放的。
- 接口是一种约束,约束实现它的类必须包含接口中声明的成员方法。
interface List {
public void add(Object data); // 接口中的方法,不写具体的实现,只用于约束。
}
// 类ArrayList实现了接口List,此时这个类就必须有一个add方法。
class ArrayList implements List {
public void add(Object data) {
// 将数据data按照连续存储的方法放在内存。
}
}
// 类LinkedList实现了接口List,此时这个类就必须有一个add方法。
class LinkedList implements List {
public void add(Object data) {
// 将数据data按照链表的形式存储
}
}
List v1 = new ArrayList();
v1.add("一个小黑");
v1.add("麻子");
List v2 = new LinkedList();
v2.add("一个小黑");
v2.add("麻子");
- List的方法和python中的列表类似,见方法名就就能知道它的含义。List中可以不指定内部存放的数据类型,那么默认存放的是Object类型,可以泛指任何数据类型,这样就和python中的列表很像了。
- LinkedList的简单用法,ArrayList用法相似,只是底层存储方式不同
import java.util.LinkedList;
public class Hello {
public static void main(String[] args) {
LinkedList<String> stringList = new LinkedList<String>();
stringList.add("一个小黑"); // 在List尾部添加元素
stringList.push("王二麻子"); // 在List头部添加元素
System.out.println(stringList.size()); // 输出List长度
stringList.set(0, "小潮院长");
for (String tmp : stringList) {
System.out.println(tmp); // 遍历输出List每个元素
}
}
}
- 迭代器的用法和python中也类似
import java.util.ArrayList;
import java.util.Iterator;
public class Hello {
public static void main(String[] args) {
ArrayList<String> s1 = new ArrayList<String>();
s1.add("一个小黑");
s1.add("小潮院长");
s1.add("Bilibili");
Iterator<String> it = s1.iterator();
while (it.hasNext()) {
String item = it.next();
System.out.println(item);
}
}
}
- 上述逆向案例就是将CustomerOperateDraftBean类型的LinkedList转化成迭代器,利用迭代器遍历里面的元素。
Set系列
- Set类似python中的集合,用于存放不重复的多元素集合。
- Set是一种接口,其下有两种实现,分别是HashSet和TreeSet,HashSet去重,无序,TreeSet去重,内部默认排序(ascii、unicode)。
- TreeSet的简单用法,HashSet用法相似,只是一个默认排序,一个无序
import java.util.Iterator;
import java.util.TreeSet;
public class Hello {
public static void main(String[] args) {
TreeSet<String> t1 = new TreeSet<String>();
t1.add("car");
t1.add("banana");
t1.add("apple");
Iterator<String> it = t1.iterator();
while (it.hasNext()) {
System.out.println(it.next()); // 输出顺序 apple, banana, car
}
}
}
上述逆向案例就是定义了一个TreeSet,然后根据一些条件往里面添加元素,然后根据里面的东西做相应的计算。
Map系列
- Map类似python中的字典,用于存放键值对。
- Map是一种接口,其下有两种实现,分别是HashMap和TreeMap,HashMap无序,TreeMap内部默认排序(ascii、unicode)。
- python里没有排序的字典,但可以通过sorted()函数将字典按键值进行排序,逆向中有时候会从TreeMap中提取键值对,并用’&'连接构成字符串,在python中需要对字典进行排序后,再连接成字符串
d = {
"aid": 123,
"xx": 999,
"wid": 888
}
result = "&".join([f'{key}={d[key]}' for key in sorted(d.keys())])
- TreeMap的简单用法,HashMap用法相似,只是一个默认排序,一个无序
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class Hello {
public static void main(String[] args) {
TreeMap<String, String> m1 = new TreeMap<String, String>();
m1.put("name", "一个小黑");
m1.put("age", "18");
m1.put("sex", "男");
// java中的Map类型不能直接转换为迭代器,必须借助Set
Set<Map.Entry<String, String>> s1 = m1.entrySet();
Iterator<Map.Entry<String, String>> it1 = s1.iterator();
while (it1.hasNext()) {
Map.Entry<String, String> entry = it1.next();
String k = entry.getKey();
String v = entry.getValue();
System.out.println(k + ": " + v);
}
// 利用for循环遍历Map
for (Map.Entry<String, String> entry : m1.entrySet()) {
String k = entry.getKey();
String v = entry.getValue();
System.out.println(k + ": " + v);
}
}
}
上述逆向案例利用for循环遍历Map中的键值对,添加到StringBuilder对象中,最后进行MD5加密。
上述逆向案例定义了一个TreeMap v4对象,往里面添加了很多键值对,最后利用StringBuilder讲键值对连接起来,并去掉最后一个’&'字符。
面向对象相关
构造方法 (重载)
- Java中,在类里和类名相同的方法叫构造方法,构造方法没有返回值,但可以有多个构造方法,通过参数列表不同进行重载,后期利用frida进行hook的时候,需要注意重载的问题。
class Person {
// 实例变量
public String name;
public int age;
// 重载构造方法1
public Person() {
this.name = "一个小黑";
this.age = 18;
}
// 重载构造方法2
public Person(String name) {
this.name = name;
this.age = 18;
}
// 重载构造方法3
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 方便打印输出,调试使用
public String toString() {
return this.name + " " + this.age;
}
}
public class Hello {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person("小潮院长");
Person p3 = new Person("小潮院长", 18);
System.out.println(p1); // 一个小黑 18
System.out.println(p2); // 小潮院长 18
System.out.println(p3); // 小潮院长 18
}
}
静态成员
- Java中,类里的成员分为静态成员和实例成员(成员可以是方法,也可以是变量),静态成员是和类绑定的,无需实例化对象就可以调用,实例成员必须通过类实例化对象后,通过对象调用。
class Person {
public static String city = "上海"; // 静态变量
public String name; // 实例变量
public Person() {
this.name = "一个小黑";
}
}
public class Hello {
public static void main(String[] args) {
System.out.println(Person.city); // 静态变量通过类名获取
Person p1 = new Person();
System.out.println(p1.name); // 实例变量通过对象获取
}
}
继承
- 和Python不同,Java中的继承只能是单继承,但可以实现多个接口。
class Base {
...
}
// 单继承
class Son extends Base {
...
}
class Base {
public void add();
}
class Foo {
public void plus();
}
// 实现多个接口
class Son implements Base, Foo {
public void add() {
}
public void plus() {
}
}
- Java简单的继承示例
// 父类
class Base {
public String email;
public Base(String email) {
this.email = email;
}
public String getSubInfo() {
return String.format("%s", this.email);
}
}
// 子类继承父类
class Person extends Base {
public String name;
public Integer age;
public Person(String name, Integer age, String email) {
super(email);
this.name = name;
this.age = age;
}
public String getInfo() {
return String.format("%s-%d-%s", this.name, this.age, this.email);
}
}
public class Hello {
public static void main(String[] args) {
Person p1 = new Person("一个小黑", 18, "xxx@qq.com");
System.out.println(p1.getInfo()); // 一个小黑-18-xxx@qq.com
System.out.println(p1.getSubInfo()); // xxx@qq.com
}
}
- Java中的父类可以泛指所有的子类,但实际上父类定义的变量取决于后面的构造方法。
- 如果用父类泛指了子类,那么调用父类和子类中同名且参数列表相同的方法时,会调用实际子类的方法。
- 如果用父类泛指了子类,那么调用子类中有但父类中没有的方法时,需要强制类型转换成子类对象才可以调用。
// 父类
class Base {
public String email;
public Base(String email) {
this.email = email;
}
public String getSubInfo() {
return String.format("%s", this.email);
}
}
// 子类继承父类
class Person extends Base {
public String name;
public Integer age;
public Person(String name, Integer age, String email) {
super(email);
this.name = name;
this.age = age;
}
public String getSubInfo() {
return String.format("%s-%d-%s", this.name, this.age, this.email);
}
public String getInfo() {
return String.format("%s-%d-%s", this.name, this.age, this.email);
}
}
public class Hello {
public static void main(String[] args) {
Person p1 = new Person("一个小黑", 18, "xxx@qq.com");
System.out.println(p1.getSubInfo()); // 一个小黑-18-xxx@qq.com
Base b1 = new Base("yyy@qq.com");
System.out.println(b1.getSubInfo()); // yyy@qq.com
// 父类泛指子类,调用父子类共有的方法时,会调用实际子类的方法
Base b2 = new Person("小潮院长", 18, "zzz@qq.com");
System.out.println(b2.getSubInfo()); // 小潮院长-18-zzz@qq.com
// 父类泛指子类,调用子类中新的方法,需要强制类型转换
Base b3 = new Person("杜海皇", 18, "ooo@qq.com");
System.out.println(((Person)(b3)).getInfo()); // 杜海皇-18-ooo@qq.com
}
}
接口
- Java中接口可以用来约束实现它的类,也可以用来泛指实现它的类
// 接口
interface IMessage {
public void send();
}
class Wechat implements IMessage {
public void send() {
System.out.println("发送微信");
}
}
class DingDing implements IMessage {
public void send() {
System.out.println("发送钉钉");
}
}
class Sms implements IMessage {
public void send() {
System.out.println("发送短息");
}
}
public class Hello {
// 参数用接口,可以泛指实现它的子类
public static void handler(IMessage v1) {
v1.send();
}
public static void main(String[] args) {
Wechat v1 = new Wechat();
DingDing v2 = new DingDing();
Sms v3 = new Sms();
handler(v1); // 发送微信
handler(v2); // 发送钉钉
handler(v3); // 发送短息
}
}
- 逆向的时候如果找到了某个方法声明在一个接口里,那么就需要去找哪些类实现了这个接口。
- 如果只有一个类实现了接口,那么这个方法就确定在这个类中。
- 如果有多个类实现了接口,那么就需要从调用的地方顺藤摸瓜,看究竟是哪个对象在调用这个方法,来确定方法的定义处。
抽象
- Java中的抽象类相当于继承+接口,抽象类中既可以替继承它的子类实现方法,也可以约束继承它的子类,必须定义一些方法,也可以泛指继承它的子类
abstract class Base {
// 抽象方法 (约束子类中必须有这个方法)
public abstract void play(String name);
// 普通方法
public void stop() {
System.out.println("Stop");
}
}
class Son extends Base {
public void play(String name) {
System.out.println(String.format("%s is plyaing", name));
}
}
public class Hello {
public static void main(String[] args) {
Base s1 = new Son(); // 抽象类也可以泛指它的子类
s1.play("一个小黑"); // 一个小黑 is plyaing
s1.stop(); // Stop
}
}
包概念
- Java中的包其实就是文件夹
src
├── Hello.java
└── utils
└── Helper.java
- utils就是一个包
import utils.Helper;
public class Hello {
public static void main(String[] args) {
System.out.println(Helper.getInfo());
}
}
package utils;
public class Helper {
public static String getInfo() {
return "一个小黑";
}
}
类的修饰符:
- public,公共(任何人都能调用包中的类)
- default,只能在当前包中被调用
类成员修饰符:
- public,公共,所有的只要有权限访问类,类中的public成员都可以被访问
- private,私有,只允许自己类调用
- protected,同一个包 或 子类可以访问(即使没有在同一个包内,也可以访问父类中的protected成员)
- default,只能在同一个包内访问