Java编程思想-笔记

第二章 一切都是对象

String a = new String("peng") 中一共创建了2个对象

在栈(栈主要保存基本类型(char、byte、short、int、long、float、double、boolean)和对象的引用)中的  引用变量 a(不算对象)

分别为在堆中的通过new的对象

和在常量池中的对象 peng

第三章 操作符

1.1 自动递增和递减

++i 和 --i 表示前缀式,会先执行运算,在生成值

i++和 i--表示后缀式,先生成值,在执行运算

优先级比+-*/更高

i=1

i+++1 表示 (i++)+1  先生成值,所以结果为1

 ++i+1 表示 (++i)+1 先运算,所以结果为2

1.2 equals()的默认行为是比较引用

1.3 逻辑运算符&& 和 || 会逻辑短路

1.4 移位操作符

都是根据补码进行移位,正数的补码为本身,负数的补码为原码的反码+1(负数的反码为原码中,除符号位以外,每一位取反)

<< 左移位操作符,补码左移对应的n位,m<<n 表示m*2^n

>> 有符号右移,补码右移后,高位都用1补充

>>>无符号右移,补码右移后,高位都用0补充

1.5 double类型和float的+,-,*,/,%

计算机中,所有十进制的数进行加减乘除等计算的时候,都是先转化为二进制计算的,因此浮点数类型转化的时候存在误差,结果会出现异常,使用BigDecimal进行操作。

double a = 0.05;
double b = 0.01;
BigDecimal c = new BigDecimal(Double.toString(a));
BigDecimal d = new BigDecimal(Double.toString(b));
System.out.println(a+b);
System.out.println(c.add(d).doubleValue());

输出为

0.060000000000000005
0.06

第五章 初始化与清理

1.1 重载和重写 

重载:方法名相同,参数不同,函数重载

重写:子类重写父类的方法

1.2 静态属性

什么是类静态属性(以及静态方法):首先静态属性和方法必须用static修饰符,static 可以修饰属性、方法、代码块、内部类

静态属性(方法)和非静态属性(方法)的区别:

1:内存中存在的位置不同,静态属性存在于方法区中,而非静态的属性存在堆中。

2:静态属性在类的加载过程中生成,而非静态属性则在创建对象之后存在。

3:由二可知,静态属性在类销毁的时候销毁,而非静态属性在对象销毁后销毁。

1.3 初始化顺序

先初始化静态对象,若父类的静态对象未初始化则先初始化父类的静态对象,然后非静态属性(非静态代码块->构造函数)若父类未初始化,则父类也先初始化。

public class ParentClassTest {

    static {
        System.out.println("parent static out");
    }

    {
        System.out.println("parent normal out ");
    }

    public ParentClassTest() {
        System.out.println("parent constructor out");
    }

}

public class ChildClassTest extends ParentClassTest{

    static {
        System.out.println("child static out");
    }

    {
        System.out.println("child normal out ");
    }

    public ChildClassTest() {
        System.out.println("child constructor out");
    }

    public static void main(String[] args) {
        ChildClassTest childClassTest = new ChildClassTest();
    }

}

输出:
parent static out
child static out
parent normal out 
parent constructor out
child normal out 
child constructor out

1.4 垃圾回收

1.4.1 回收不被gc root引用的对象

1.4.2 哪些对象可以作为gc root

1.虚拟机栈中引用的对象

2.方法区中常量引用的对象

3.方法区中类静态属性引用的对象

4.本地方法栈中JNI(即一般说的native方法)引用的对象

第六章 访问权限控制

访问权限含义本类本包的类子类非子类的外包类
public公共
protected保护
default(默认 在不写对应的访问权限即为default)包访问权限
private私有的

第九章 接口

在java8以后,接口中可以添加使用default或者static修饰的方法,default方法表示默认方法,父类实现接口若未重写,则直接调用接口中的默认方法。

public interface DefaultInterfaceTest {

    default void test() {
        System.out.println("test");
    }

}
public class DefaultClass implements DefaultInterfaceTest{

    public static void main(String[] args){
        new DefaultClass().test();
    }
}

第十一章 持有对象

1 for,foreach Iterator

在ArrayList中由于内部为数组,则通过for指定查找对应复杂度为O(1),在LinkedList内部为链表,通过for查找效率低O(n),但是通过Iterator查找效率为O(1)。

在forEach中不能直接remove,因为list的remove不会修改对应的expectedModCount

forEach反编译后实际为调用Iterator

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
for (Integer value:list){
    logger.debug(value);
}
反编译后
List<Integer> list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
Iterator var2 = list.iterator();
while(var2.hasNext()) {
    Integer value = (Integer)var2.next();
    logger.debug(value);
}

2 反编译

通过jdk自带的工具javap来反编译

第十二章 通过异常处理错误

1.1 throwable

分为Error和Exception,error表示错误通常是指程序无法恢复的异常,而exception表示程序有可能恢复的异常情况。

1.2 通过catch捕获异常

catch能通过对异常的向上捕获,表示可以由父类捕获,这也是为什么Exception能捕获到所有异常的原因,

1.2 finally的执行

1、finally无论如何都会执行,除非特殊情况如调用System.exit()

2、finally中抛出异常可能会导致异常丢失,即finally中抛出另外一个异常,会导致原先catch的异常方法无法执行

3、在try或catch执行到了return指令后,代表该函数已经结束并跳出函数体,任何语句都执行在return前,因此finally中的代码也是执行在了return前,但此时会对return的结果放入一个临时的栈空间,如果为基本类型则为该值,若为引用变量则为该引用,若finally中执行了基本类型的数据修改,但是未进行return那么临时栈空间中的值未被更新;若进行了return那么临时栈空间中的值就进行了更新;若为引用变量那么不论是否return,只要修改了变量则最终try中return的值就会修改

public class FinallyTest {

    public StringBuilder test() {
        StringBuilder tmp = new StringBuilder();
        try {
            tmp.append(1);
             return tmp.append(2);
        } catch (Exception e) {
            tmp.append(4);
        } finally {
            tmp.append(3);
            return tmp;
        }
    }

    public static void main(String[] args) {
        FinallyTest finallyTest = new FinallyTest();
        System.out.println(finallyTest.test());
    }


}

输出123

第十三章 字符串

1 String不可变

String类通过final修饰

2  String的+在反编译后实际为通过StringBuilder方式执行

第十四章 类型信息

1 容器

任何一种数据类型或者对象放进容器中后都会失去原有的类型,变成 Object,用的时候从容器中取出后进行转型.如ArrayList中存储数组为Object[] elementData;

2 RTTI : 包括传统RTTI (Run-Time Type Identification 运行类型识别)和反射

传统RTTI它假定我们在编译时已经知道了所有的类型,反射允许我们在运行时发现和使用类的信息

书中有一个代码例子:

public class Shapes{
	public static void main(String args[]){
		List<Shape> shapleList= Arrays.aslist(new Circle(),new Square(),new Triangle());
        for (Shape shape : shapleList) {
            shape.draw();
        }
	} 
}

基类包含了draw的方法,通过toString()进行派生类覆盖。

数组放入元素时会向上转型,丢失了Shape对象的具体类型。

当Java创建某个类的对象,比如Circle类对象时,Java会检查内存中是否有相应的Class对象。如果内存中没有相应的Class对象,那么Java会在.class文件中寻找Circle类的定义,并加载Circle类的Class对象。

一旦Class对象加载成功,就可以用它来创建这种类型的所有对象。这也就是说,每个对象在运行时都会有对应的Class对象,这个Class对象包含了这个对象的类型信息。因此,我们能够通过Class对象知道某个对象“真正”的类型,并不会因为向上转型而丢失。

当从数组取出元素时,容器实质上以所有事物都当中Object持有,自动将结果转型为Shape(RTTI最基本的使用形式,所有的类型转换都是在运行时进行正确性检查,名字含义:在运行时,识别一个对象的类型)

而具体Shape实际执行什么代码,是多态的机制,由引用所指向的具体对象来决定。

3 Class

Class.forName("name");返回一个Class对象的引用,如果类还没加载就加载它。实际调用的是ClassLoad.loadClass("name",true);

当使用.class来创建对Class对象的引用时,不会自动地初始化该Class对象。

ClassLoad.loadClass("name");实际调用的是ClassLoad.loadClass("name",false);

4 RTTI的3中形式

1、传统的类型转换:在运行时,识别一个对象的类型,如Object变为Shape

2、代表对象的类型的Class对象。通过查询Class对象可以获取运行时所需的信息

3、关键字instanceof

5 动态代理与静态代理

代理,将本身A需要做的事情,交给了别人做,如A要取快递,B也要取快递,那么B帮A代理了拿快递的事情,但是B也要做自己的事情,那么B就是代理类,A就是被代理类。

1、静态代理

代理类和被代理类共同实现一个接口,或者是共同继承某个类。代码案例:

public interface StaticProxyInterface {

    void doSth();

}

// 被代理的类
public class NewJob implements StaticProxyInterface{
    @Override
    public void doSth() {
        System.out.println("doing sth");
    }
}


// 代理类
public class StaticProxy implements StaticProxyInterface{

    NewJob newJob;

    public StaticProxy(NewJob newJob) {
        this.newJob = newJob;
    }

    @Override
    public void doSth() {
        before();
        newJob.doSth();
        after();
    }

    private void before() {
        System.out.println("before");
    }

    private void after() {
        System.out.println("after");
    }


}

代理类可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。

为什么叫静态代理,是因为代理类是事先准备完成的,而不是通过动态生成的。

2 动态代理

cglb动态代理:在没有实现接口的情况下,也能实现的代理(通过字节码技术,为一个类创建子类,  在子类中采用方法拦截,拦截了所有父类方法的调用,  并加入横切的逻辑.)

public class Job {

    public void doSth() {
        System.out.println("doing sth");
    }

}

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibInterceptor implements MethodInterceptor {


    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("执行前...");
        //通过代理类调用父类中的方法
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("执行后...");
        return result;
    }
}


测试方法
import org.springframework.cglib.proxy.Enhancer;

public class Test {

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        //设置需要创建子类的类
        enhancer.setSuperclass(Job.class);
        enhancer.setCallback(new CglibInterceptor());
        //通过字节码技术动态创建子类实例
        Job newJob = (Job) enhancer.create();
        newJob.doSth();
    }

}

 

jdk动态代理:只有在实现接口的情况下,才能实现的代理 具体实现步骤:https://blog.csdn.net/qq_38340127/article/details/93759953

两者差距:

  •  JDK 实现动态代理:通过jdk提供的工具方法Proxy.newProxyInstance动态构建全新的代理类(继承Proxy类,并持有InvocationHandler接口引用 )字节码文件并实例化对象返回。(jdk动态代理是由java内部的反射机制来实例化代理对象,并代理的调用委托类方法)
  • CGlib 动态代理模式: 基于继承被代理类生成代理子类,不用实现接口。只需要被代理类是非final 类即可。(cglib动态代理底层是借助asm字节码技术

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值