这里看到实例化对象的时候用的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();
}
}
String str = new String(“abc”);至少会创建一个对象,也有可能创建两个。因为用到new关键字,肯定会在堆中创建一个String对象,如果字符池中已经存在”abc”,则不会在字符串池中创建一个String对象,如果不存在,则会在字符串常量池中也创建一个对象。简单点说就是new了多少个,就有多少个不同的对象。
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);
前言
调链子前补了很多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中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问
继承
其实每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。
初学反射比较乱的笔记
Class.forName()中传入的参数就是类的全限定名,返回的是类对象
注意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[]返回参数类型数组
为了搞懂反射自己尝试写了一个类
Runtime类的功能如下:
看到Runtime类容易想到与命令执行有关,但是他具体怎么实现的,来剖析一下
先看这一个简单的代码
首先要知道Runtime类是一个特殊的类,他的实例化不是通过new来实例化的,他是通过调用getRuntime()这个方法来实例化。梳理一下,这里直接用Runtime.getRuntime()定义一个Runtime类型的实例cmd,现在cmd是一个Runtime类的实例了,然后调用里面的exec方法来弹了个计算器。
在看一个反射来执行的例子。
再来梳理一下,首先
Runtime.class
是获取类对象,为什么可以这样获取是反射的内容了,就不过多介绍了,然后继续getMethod去获取里面的getRuntime方法,接着调用这个方法,让r成为一个Runtime的实例,这里先看一下invoke方法的介绍由于getRuntime是静态方法,所以直接调用了
然后用
Runtime.class.getMethod
去获取了Runtime
类里面的exec
方法,再看一下getMethod方法的参数传递由于exec传入的肯定是字符串,所以后面要跟一个String.class,最后一步就是执行exec方法了,由于是反射得到的,也是用invoke方法实现了弹计算器。
向上向下转型
还是补基础
首先为什么主方法要有static才可以运行,因为所有类都必须要实例化才可以使用,不实例化就想使用的话需要有static才可以运行
这里看到实例化对象的时候用的Animal类,意思是说实例化的对象dog是属于animal类的,这里当然可以用Dog dog = new Dog();来表示,但这样表示的话表示的范围更广,简单来说,如果前面的类名用Dog的话,实例化的对象将只能是Dog();如果是animal的话,那也可以实例化Cat();
包括用接口的话也是一个道理,如果有两个类都继承了1个接口的话,那同样用 接口名 对象名 = new 类名。 这样的话类名就可以使用两个继承了接口的类,范围会更大一点。
再比如我定义了一个接口
再有一个实现类
可以看到我实例化变量的时候用的类名是Hello,也就是说new的a被定义为了Hello类赋给了b变量,之前不太明白,后面看了这里其实
向上转型和向下转型
的知识,这里改成了实现接口的形式,这样定义叫做接口的回调
,其实和继承中向上转型是很类似的。就不做过多介绍了。所以说最后调用b.ha()是肯定会报错的。
Java中的Process类
用Java编写应用时,有时需要在程序中调用另一个现成的可执行程序或系统命令,这时可以通过组合使用Java提供的Runtime类和Process类的方法实现。
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
而我们在写
payload
的时候需要对里面的属性赋值,但是里面变量都是private
属性,set方法也是private
属性,如果方法是public
的话我们还可以通过公共方法来对私有属性赋值,但是方法也是private
的,这种情况的话我们直接对变量赋值是没有用的了,那就只能用到反射了。在写
exp
时为了方便起见,我们把反射后对变量赋值的所有语句写成一个函数,先看再来慢慢分析首先这个基础要明白
所以这里用的
getDeclaredField
,obj.getClass()
很好理解,就是获取你想要反射的类对象,然后用getDeclaredField
获取类对象的所有字段。不过要注意的是这里只能获取到
private
的字段,但并不能访问该private
字段的值,除非加上setAccessible(true)
,第三个句子就是简单的赋值了。来编写一下field.get(obj)
是字段的值,obj
是拥有字段的对象,我把它输出出来好直观的看成功没我们在稍微改一下,把题目中的构造函数改成
private
属性的这样的话直接
new
他肯定也是不行的,所以这里想得到DatabaseInfo
对象也得通过反射来获取,还是先获取他的class
对象然后获取类对象的构造函数
在
getDeclaredConstructor
里面是可以传参的,传入的参数应该是构造器中存在且可以传的参数类型,比如里面有个构造器是意思是需要传入一个info类型的变量,那么刚刚哪个语句就应该是
同样在
newInstance
时也应该往里面传入该类型的对象回到刚刚说的那里,接着就是调用构造函数了,还是需要
setAccessible(true)
让private
可以被访问,然后newInstance();
就调用成功了,因为他原来的构造函数中就没有传参,所以我们在newInstance
中也不需要传参exp:
最后看一下成果