前言

调链子前补了很多java基础,笔记写的很烂很散。。大部分只是记录学习过程中自己薄弱的部分,也是简化了很多为了自己能看懂,所以这部分没什么参考价值,不过是随时更新的~

检测字符串是否相等

equals方法可以检测:s.equals(t)

==会比较地址

静态方法

“静态方法和属性是属于类的,调用的时候直接通过类名.方法名完成对,不需要继承机制及可以调用。

空串与NULL串

空串是一个对象和NULL不同

码点与代码单元

码点直接看多少个字符就可以了

代码单元会组成码点

类和对象

构造器的名字应该与类名相同。在构造器前面加上new表示创建了一个对象

System.out.println(new Date());

String s = new Date().toString;

System.out.println(s);

是一样的,因为输出对象的时候也会调用tostring方法。

对象变量只是引用一个对象,并不包含一个对象

构造器与类同名

每个类可以有一个以上的构造器

构造器可以有0个,1个或多个参数

构造器没有返回值

构造器总是伴随着new操作符一起调用的

静态字段属于类不属于对象

java修饰符

在java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问

  • default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
  • private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
  • public : 对所有类可见。使用对象:类、接口、变量、方法
  • protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)

继承

其实每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。

初学反射比较乱的笔记

Class.forName()中传入的参数就是类的全限定名,返回的是类对象

image-20220323220010551

注意forname获得的是某个类的class对象,而不是那个类的对象

getclass获取的是cls的运行类型,也就是定义cls这个对象的类型 也就是class这个类 //java.lang.Class

java.io.ObjectStreamField.getName() 获取该字段的名称。

获取class类对象

1.编译阶段可以用Class.forname()获取类对象 多用于配置文件,读取类全路径加载类,一般要知道类的全路径才可以这样用

2.newInstance创建类是这个类必须已经加载过且已经连接,new创建类是则不需要这个类加载过

3.Class类加载阶段,Class类对象已经存在了直接类.class可以获取 多用于参数传递

运行阶段可以因为已经存在了对象,可以直接用对象.getClass获取

类加载

静态加载:编译时加载相关类,如果没有则报错,依赖性太强 用new是静态加载,必须编写这个类

动态加载:反射获取的类是动态加载的,也就是说编译可以过,运行到的时候才会去架子啊 降低依赖性

类加载5个阶段

第一个阶段:

加载阶段,将字节码从不同数据源转化为2进制字节流加载到内存中,并生成一个代表该类的类对象。

第二个阶段:

连接阶段 验证

为了确保class文件的字节流包含的信息符合当前虚拟机的要求,(包括文件格式,元数据,字节码)

第三个阶段:

连接阶段 准备

jvm会在该阶段对静态变量,分配内存并默认初始化,这些变量所使用的内存都将在方法区中分配

第四个阶段:

连接阶段 解析

把常量的符号引用替换为内存地址的引用

第五个阶段:

初始化

通过反射获取类的基本信息

第一组:java.lang.reflect.Class类

Java直接输出一个类的对象的时候,会自动调用这个类的toString方法。

java.lang.reflect.Field.getName()方法返回此Field对象表示的字段的名称。

第二组:java.lang.reflect.Field类

getMethodfilers:以int形式返回修饰符

说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16

getType:以Class形式返回类型(属性的类型对应的class对象)

getName:

第三组:java.lang.reflect.Method类

getModifiers:以int形式返回修饰符

说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16

getreturnType:以Class形式获取返回类型

getName:返回方法名

getParameterTypes:以class[]返回参数(形参)类型数组

第四组:java.lang.reflect.Constructor类

1.getModifiers:以int返回修饰符

2.getName:返回构造器名,全类名

3.getParameterTypes:以Class[]返回参数类型数组

为了搞懂反射自己尝试写了一个类

package com.baidu;

import org.junit.Test;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class b {
    public static void main(String[] args){

    }

    //第一组方法API
    @Test
    public void api_1() throws ClassNotFoundException {
        //得到类Class类对象
        Class personCls = Class.forName("com.baidu.Person");
        //getName:获取全类名
        System.out.println(personCls.getName());

        //getSimpleName:获取简单类名
        System.out.println(personCls.getSimpleName());
        //getFields:获取所有public修饰的属性,包含本类以及父类的
        Field[] fields = personCls.getFields();
        for (Field field : fields) {
            System.out.println("本类及父类的属性=" + field.getName() + field.getModifiers());
        }
        System.out.println("------------------------------");
        //getDeclareFields:获取本类中所有属性

        Field[] declaredFields = personCls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本类的所有属性" + declaredField.getName());
        }

        System.out.println("---------------------");
        //getMethods:获取所有public修饰的方法,包含本类以及父类的
        Method[] methods = personCls.getMethods();
        for (Method method : methods) {
            System.out.println("本类及父类的method=" + method.getName());
        }
        System.out.println("------------------------");
        //getDeclaredMethods:获取本类中所有方法
        Method[] declaredMethods = personCls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("获取本类的所有方法" + declaredMethod.getName());
        }
        //getConstructors:获取所有public修饰的构造器,包含本类  没有父类,因为构造器不能继承
        //getDeclaredConstructors:获取本类中所有的构造器
        //getPackage:以package形式返回包信息
        //getSuperClass:以Class形式返回父类信息
        //getInterfaces:以Class[]形式返回接口信息
        //getAnnotations:以Annotation[]形式返回注解信息


    }
}

class A {
    public String hobby;
    public void hi(){

    }
}

class Person extends A {
    //属性
    public String name;
    protected int age;
    String job;
    private double sal;
    //方法
    public void m1(){

    }
    protected void m2(){

    }
    void m3() {

    }
    private void m4(){

    }
}

Runtime类的功能如下:

查看系统内存

终止JVM虚拟机

运行系统程序

使用关闭钩子

看到Runtime类容易想到与命令执行有关,但是他具体怎么实现的,来剖析一下

import java.io.IOException;

public test {
    public static void main(String[] args) throws IOException {
        Runtime cmd = Runtime.getRuntime();
        cmd.exec("/System/Applications/Calculator.app/Contents/MacOS/Calculator");
    }
}

先看这一个简单的代码

首先要知道Runtime类是一个特殊的类,他的实例化不是通过new来实例化的,他是通过调用getRuntime()这个方法来实例化。梳理一下,这里直接用Runtime.getRuntime()定义一个Runtime类型的实例cmd,现在cmd是一个Runtime类的实例了,然后调用里面的exec方法来弹了个计算器。

在看一个反射来执行的例子。

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public test {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException {
        Method m = Runtime.class.getMethod("getRuntime");
        Runtime r = (Runtime) m.invoke(null);
        Method execMethod = Runtime.class.getMethod("exec", String.class);
        execMethod.invoke(r,"/System/Applications/Calculator.app/Contents/MacOS/Calculator");
    }
}

再来梳理一下,首先Runtime.class是获取类对象,为什么可以这样获取是反射的内容了,就不过多介绍了,然后继续getMethod去获取里面的getRuntime方法,接着调用这个方法,让r成为一个Runtime的实例,这里先看一下invoke方法的介绍

invoke方法的第一个参数是一个对象。此对象可以为:①方法持有者;②方法持有者的继承者。如果该方法是静态方法,可以用null或者用类来代替,
第二个参数是变长的,是调用该方法的参数

由于getRuntime是静态方法,所以直接调用了

然后用Runtime.class.getMethod去获取了Runtime类里面的exec方法,再看一下getMethod方法的参数传递

第一个参数是要获得方法的名字,第二个参数是按声明顺序标识该方法形参类型。(也就是类对象)

由于exec传入的肯定是字符串,所以后面要跟一个String.class,最后一步就是执行exec方法了,由于是反射得到的,也是用invoke方法实现了弹计算器。

向上向下转型

package main.java.com.baidu;

class Animal {
    void eat() {
        System.out.println("eating");
    }
    void run(){
        System.out.println("running");
    }

}
class Dog extends Animal {
    void run(){
        System.out.println("dog running");
    }
}
class Cat extends Animal{
    void run(){
        System.out.println("cat running");
    }
}

public class AnimalDemo {
    public static void main(String[] args){
        Animal dog = new Dog();
        Animal cat = new Cat();
        dog.run();
        dog.eat();
        cat.run();
        cat.eat();
    }
}

还是补基础

首先为什么主方法要有static才可以运行,因为所有类都必须要实例化才可以使用,不实例化就想使用的话需要有static才可以运行

这里看到实例化对象的时候用的Animal类,意思是说实例化的对象dog是属于animal类的,这里当然可以用Dog dog = new Dog();来表示,但这样表示的话表示的范围更广,简单来说,如果前面的类名用Dog的话,实例化的对象将只能是Dog();如果是animal的话,那也可以实例化Cat();

包括用接口的话也是一个道理,如果有两个类都继承了1个接口的话,那同样用 接口名 对象名 = new 类名。 这样的话类名就可以使用两个继承了接口的类,范围会更大一点。

再比如我定义了一个接口

package com.c1oud;

public interface Hello {
    public void hi();
}

再有一个实现类

package com.c1oud;

public class a implements Hello{
    public void hi(){
        System.out.println("123");
    }
    public void ha(){
        System.out.println("zzz");
    }
    public static void main(String[] args){
        Hello b = new a();
        b.ha();
    }

}

可以看到我实例化变量的时候用的类名是Hello,也就是说new的a被定义为了Hello类赋给了b变量,之前不太明白,后面看了这里其实向上转型和向下转型的知识,这里改成了实现接口的形式,这样定义叫做接口的回调,其实和继承中向上转型是很类似的。就不做过多介绍了。

所以说最后调用b.ha()是肯定会报错的。

Java中的Process类

用Java编写应用时,有时需要在程序中调用另一个现成的可执行程序或系统命令,这时可以通过组合使用Java提供的Runtime类和Process类的方法实现。

Process process = Runtime.getRuntime().exec(“.\p.exe”); 
process.waitfor( ); “.\p.exe”是要执行的程序名 waitfor()目的为等待子进程完成再往下执行

JAVA内部类

顾名思义,JAVA内部类就是在类里面定义的类

在类内部可定义成员变量和方法,且在类内部也可以定义另一个类。如果在类 Outer 的内部再定义一个类 Inner,此时类 Inner 就称为内部类(或称为嵌套类),而类 Outer 则称为外部类(或称为宿主类)。

内部类可以很好地实现隐藏,一般的非内部类是不允许有 private 与 protected 权限的,但内部类可以。内部类拥有外部类的所有元素的访问权限。

内部类可以分为:实例内部类、静态内部类和成员内部类,每种内部类都有它特定的一些特点,本节先详细介绍一些和内部类相关的知识。

在类 A 中定义类 B,那么类 B 就是内部类,也称为嵌套类,相对而言,类 A 就是外部类。如果有多层嵌套,例如类 A 中有内部类 B,而类 B 中还有内部类 C,那么通常将最外层的类称为顶层类(或者顶级类)。

内部类的特点如下:

内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号。
内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否为 private 的。
内部类声明成静态的,就不能随便访问外部类的成员变量,仍然是只能访问外部类的静态成员变量。

所以我理解为内部类常用反射来获取

http://c.biancheng.net/view/1022.html

String str=new String("a")和String str = "a"的区别

String str = “abc”;可能创建一个或者不创建对象,如果”abc”在字符串池中不存在,会在java字符串池中创建一个String对象(”abc”),然后str指向这个内存地址,无论以后用这种方式创建多少个值为”abc”的字符串对象,始终只有一个内存地址被分配

String str = new String(“abc”);至少会创建一个对象,也有可能创建两个。因为用到new关键字,肯定会在堆中创建一个String对象,如果字符池中已经存在”abc”,则不会在字符串池中创建一个String对象,如果不存在,则会在字符串常量池中也创建一个对象。简单点说就是new了多少个,就有多少个不同的对象。

写payload的时遇到的反射

很多时候在写payload的时候,题目中变量的属性并不是public,并且也没有public方法可以设置保护属性,这个时候就会遇到反射了,用羊城杯那道JDBC的反序列列题为例:

稍微改了一下题目代码,因为只探究反射的内容

假如题目是这样,只有构造函数是public属性,其他都是private

public class DatabaseInfo {
    private String host;
    private String port;
    private String username;
    private String password;
    public DatabaseInfo(){
        System.out.println("success!");

    }
    private void setHost(String host){this.host=host;}
    private void setPort(String port){this.port=port;}
    private void setUsername(String username){this.username=username;}
    private void setPassword(String password){this.password=password;}

}

而我们在写payload的时候需要对里面的属性赋值,但是里面变量都是private属性,set方法也是private属性,如果方法是public的话我们还可以通过公共方法来对私有属性赋值,但是方法也是private的,这种情况的话我们直接对变量赋值是没有用的了,那就只能用到反射了。

在写exp时为了方便起见,我们把反射后对变量赋值的所有语句写成一个函数,先看再来慢慢分析

    public static void setFieldValue(Object obj,String fieldname,Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(fieldname);
        field.setAccessible(true);
        field.set(obj,value);

首先这个基础要明白

getDeclaredField是可以获取当前类本身的所有字段,不包括继承的

getField只能获取当前类及其父类的public 字段,包括继承的

所以这里用的getDeclaredFieldobj.getClass()很好理解,就是获取你想要反射的类对象,然后用getDeclaredField获取类对象的所有字段。不过要注意的是

这里只能获取到private的字段,但并不能访问该private字段的值,除非加上setAccessible(true),第三个句子就是简单的赋值了。来编写一下

import java.lang.reflect.Field;

public class reflect {
    public static void setFieldValue(Object obj,String fieldname,Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(fieldname);
        field.setAccessible(true);
        field.set(obj,value);
        System.out.println(field.get(obj));
    }

    public static void main(String[] args) throws Exception{
        DatabaseInfo a = new DatabaseInfo();
        setFieldValue(a,"host","123.321.313.3");
        setFieldValue(a,"port","23");
        setFieldValue(a,"username","c1");
        setFieldValue(a,"password","zzz");

    }
}

field.get(obj)是字段的值,obj是拥有字段的对象,我把它输出出来好直观的看成功没

我们在稍微改一下,把题目中的构造函数改成private属性的

public class DatabaseInfo {
    private String host;
    private String port;
    private String username;
    private String password;
    private DatabaseInfo(){
        System.out.println("success!");

    }
    private void setHost(String host){this.host=host;}
    private void setPort(String port){this.port=port;}
    private void setUsername(String username){this.username=username;}
    private void setPassword(String password){this.password=password;}

}

这样的话直接new他肯定也是不行的,所以这里想得到DatabaseInfo对象也得通过反射来获取,还是先获取他的class对象

 Class clazz = Class.forName("DatabaseInfo");

然后获取类对象的构造函数

Constructor constructor = clazz.getDeclaredConstructor();

getDeclaredConstructor里面是可以传参的,传入的参数应该是构造器中存在且可以传的参数类型,比如里面有个构造器是

 private DatabaseInfo(Info a){
        System.out.println("success!");

    }

意思是需要传入一个info类型的变量,那么刚刚哪个语句就应该是

Constructor constructor = clazz.getDeclaredConstructor(Info.class);

同样在newInstance时也应该往里面传入该类型的对象


回到刚刚说的那里,接着就是调用构造函数了,还是需要setAccessible(true)private可以被访问,然后newInstance();就调用成功了,因为他原来的构造函数中就没有传参,所以我们在newInstance中也不需要传参

constructor.setAccessible(true);
        Object a = constructor.newInstance();

exp:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class reflect {
    public static void setFieldValue(Object obj,String fieldname,Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(fieldname);
        field.setAccessible(true);
        field.set(obj,value);
        System.out.println(field.get(obj));
    }

    public static void main(String[] args) throws Exception{
        Class clazz = Class.forName("DatabaseInfo");
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        Object a = constructor.newInstance();



        setFieldValue(a,"host","123.321.313.3");
        setFieldValue(a,"port","23");
        setFieldValue(a,"username","c1");
        setFieldValue(a,"password","zzz");

    }
}

最后看一下成果