学习

javaGuide基础1

转载自https://github.com/Snailclimb/JavaGuide (添加小部分笔记)感谢作者!

基础概念及常识 #

  • Java语言特点

    • 面向对象(封装、继承、多态)
    • 平台无关性(Java虚拟机)
    • 等等
  • JVM并非只有一种,只要满足JVM规范,可以开发自己专属JVM

  • JDK与JRE

    • JDK,JavaDevelopmentKit,包含JRE,还有编译器(javac)和工具(如javadoc、jdb)。能够创建和编译程序
    • JRE,Java运行时环境,包括Java虚拟机、Java类库,及Java命令等。但是不能创建新程序
  • 字节码,采用字节码的好处

    • Java中,JVM可以理解的代码称为字节码(.class文件),不面向任何处理器,只面向虚拟机
    • Java程序从源代码到运行的过程 ly-20241212141922068
      • java代码必须先编译为字节码,之后呢,.class–>机器码,这里JVM类加载器先加载字节码文件,然后通过解释器进行解释执行(也就是字节码需要由Java解释器来解释执行)
      • Java解释器是JVM的一部分
  • 编译与解释并存

    • 编译型:通过编译器将源代码一次性翻译成可被该平台执行的机器码,执行快、开发效率低
    • 解释型:通过解释器一句一句的将代码解释成机器代码后执行,执行慢,开发效率高
    • 如图 ly-20241212141922280
  • 为什么说 Java 语言“编译与解释并存”?

    这是因为 Java 语言既具有编译型语言的特征,也具有解释型语言的特征。因为 Java 程序要经过先编译,后解释两个步骤,由 Java 编写的程序需要先经过编译步骤,生成字节码(.class 文件),这种字节码必须由 Java 解释器来解释执行

  • Java与C++区别

    • 没学过C++,Java不提供指针直接访问内存
    • Java为单继承;但是Java支持继承多接口
    • Java有自动内存管理垃圾回收机制(GC),不需要程序员手动释放无用内存
  • 注释分为 单行注释、多行注释、文档注释 ly-20241212141922440

  • 标识符与关键字 标识符即名字,关键字则是被赋予特殊含义的标识符

  • 自增自减运算符 当 b = ++a 时,先自增(自己增加 1),再赋值(赋值给 b);当 b = a++ 时,先赋值(赋值给 b),再自增(自己增加 1)

  • continue/break/return

    • continue :指跳出当前的这一次循环,继续下一次循环。
    • break :指跳出整个循环体,继续执行循环下面的语句。
    • return 用于跳出所在方法,结束该方法的运行。
  • 变量

    • 成员变量和局部变量
      • 成员变量可以被 public,private,static 等修饰符所修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰
      • 从变量在内存中的存储方式来看,如果成员变量是使用 static 修饰的,那么这个成员变量是属于类的,如果没有使用 static 修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。
      • 从变量在内存中的生存时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动生成,随着方法的调用结束而消亡(即方法栈弹出后消亡)。
      • final必须显示赋初始值,其他都自动以类型默认值赋值
    • 静态变量:被类所有实例共享
  • 字符型常量与字符串常量区别

    ...

作用域及事务

四种作用域 #

  • singleton:默认值,当IOC容器一创建就会创建bean实例,而且是单例的,每次得到的是同一个
  • prototype:原型的,IOC容器创建时不再创建bean实例。每次调用getBean方法时再实例化该bean(每次都会进行实例化)
  • request:每次请求会实例化一个bean
  • session:在一次会话中共享一个bean

事务 #

事务是什么 #

逻辑上的一组操作,要么都执行,要么都不执行

事务的特性 #

ACID

  • Atomicity /ˌætəˈmɪsəti/原子性 , 要么全部成功,要么全部失败
  • Consistency /kənˈsɪstənsi/ 一致性 , 数据库的完整性
  • Isolation /ˌaɪsəˈleɪʃn/ 隔离性 , 数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致 , 这里涉及到事务隔离级别
  • Durability /ˌdjʊərəˈbɪləti/ 持久性 , 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失

Spring支持两种方式的事务管理 #

  • 编程式事务管理 /ˈeksɪkjuːt/ execute
    使用transactionTemplate

    @Autowired
    private TransactionTemplate transactionTemplate;
    public void testTransaction() {
    
            transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                @Override
                protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
    
                    try {
    
                        // ....  业务代码
                    } catch (Exception e){
                        //回滚
                        transactionStatus.setRollbackOnly();
                    }
    
                }
            });
    }
    

    使用transactionManager

    ...

成员变量与局部变量

代码 #

   static int s;
    int i;
    int j;

    {
        int i = 1;
        i++;
        j++;
        s++;
    }

    public void test(int j) {
        j++;
        i++;
        s++;
    }

    public static void main(String[] args) {
        Exam5 obj1 = new Exam5();
        Exam5 obj2 = new Exam5();
        obj1.test(10);
        obj1.test(20);
        obj2.test(30);

        System.out.println(obj1.i + "," + obj1.j + "," + obj1.s);
        System.out.println(obj2.i + "," + obj2.j + "," + obj2.s);
    }

运行结果 #

2,1,5
1,1,5

分析 #

就近原则 #

代码中有很多修改变量的语句,下面是用就近原则+作用域分析的图 ly-20241212141838186

...

递归与迭代

编程题 #

有n步台阶,一次只能上1步或2步,共有多少种走法

分析 #

  • 分析
    n = 1,1步 f(1) = 1
    n = 2, 两个1步,2步 f(2) = 2
    n = 3, 分两种情况: 最后1步是2级台阶/最后1步是1级台阶, 即 f(3) = f(1)+f(2) n = 4, 分两种情况: 最后1步是2级台阶/最后1步是1级台阶, 即f(4) = f(2)+f(3)

    也就是说,不管有几(n)个台阶,总要分成两种情况:最后1步是2级台阶/最后1步是1级台阶,即 f(n)= f(n-2) + f(n-1)

递归 #

      public static int f(int n){
            if(n==1 || n==2){
                return n;
            }
            return f(n-2)+f(n-1);
      }
  
        public static void main(String[] args) {
            System.out.println(f(1)); //1
            System.out.println(f(2)); //2
            System.out.println(f(3)); //3
            System.out.println(f(4)); //5
            System.out.println(f(5)); //8
        }
  • debug调试 方法栈 f(4)—->分解成f(2)+f(3) f(2)—返回- f(3)—f(2)返回—f(1)返回 【f(3)分解成f(2)和f(1)】 方法栈的个数: ly-20241212141840077

使用循环 #

    public static int loop(int n){

        if (n < 1) {
            throw new IllegalArgumentException(n + "不能小于1");
        }
        if (n == 1 || n == 2) {
            return n;
        }
        int one=2;//最后只走1步,会有2种走法
        int two=1;//最后走2步,会有1种走法
        int sum=0;
        for(int i=3;i<=n;i++){
            //最后跨两级台阶+最后跨一级台阶的走法
            sum=two+one;
            two=one;
            one=sum;
        }
        return sum;
    }

ly-20241212141840386

...

方法的参数传递机制

代码 #

public class Exam4 {
    public static void main(String[] args) {
        int i = 1;
        String str = "hello";
        Integer num = 2;
        int[] arr = {1, 2, 3, 4, 5};
        MyData my = new MyData();

        change(i, str, num, arr, my);
        System.out.println("i = " + i);
        System.out.println("str = " + str);
        System.out.println("num = " + num);
        System.out.println("arr = " + Arrays.toString(arr));
        System.out.println("my.a = " + my.a); 
    }

    public static void change(int j, String s, Integer n, int[] a,
                              MyData m) {
        j+=1;
        s+="world";
        n+=1;
        a[0]+=1;
        m.a+=1;
    }

}

结果

...

类、实例初始化

代码 #

 public class Son extends Father{
	private int i=test();
	private static int j=method();
	static {
		System.out.print("(6)");
	}
	Son(){
		System.out.print("(7)");
	}
	{
		System.out.print("(8)");
	}
	public int test(){
		System.out.print("(9)");
		return 1;
	}

	public static int method(){
		System.out.print("(10)");
		return 1;
	}

	public static void main(String[] args) {
		Son s1=new Son();
		System.out.println();
		Son s2=new Son();
	}


}
 public class Father {
	private int i=test();
	private static int j=method();
	static {
		System.out.print("(1)");
	}
	Father(){
		System.out.print("(2)");
	}
	{
		System.out.print("(3)");
	}

	public int test() {
		System.out.print("(4)");
		return 1;
	}

	public static int method() {
		System.out.print("(5)");
		return 1;
	}
}

输出:

...

单例设计模式

特点 #

  • 该类只有一个实例
    • 构造器私有化
  • 该类内部自行创建该实例
    • 使用静态变量保存
  • 能向外部提供这个实例
    • 直接暴露
    • 使用静态变量的get方法获取

几大方法 #

饿汉式 #

随着类的加载进行初始化,不管是否需要都会直接创建实例对象

public class Singleton1 {
	public static final Singleton1 INSTANCE=new Singleton1();
	private Singleton1() {
	}

}

枚举 #

枚举类表示该类型的对象是有限的几个

public enum  Singleton2 {
	 INSTANCE
}

使用静态代码块 #

随着类的加载进行初始化

public class Singleton2 {
	public static final Singleton2 INSTANCE;

	static {
		INSTANCE = new Singleton2();
	}

	private Singleton2() {
	}

}

如图,当初始化实例时需要进行复杂取值操作时,可以取代第一种方法 ly-20241212141841430

懒汉式 #

  • 延迟创建对象

    public class Singleton4 {
    	//为了防止重排序,需要添加volatile关键字
    	private static volatile Singleton4 INSTANCE;
    
    	private Singleton4() {
    	}
    
    	/**
    	 * double check
    	 * @return
    	 */
    	public static Singleton4 getInstance() {
    		//2 先判断一次,对于后面的操作(此时已经创建了对象)能减少加锁次数
    		if (INSTANCE == null) {
    			//如果这里不加锁会导致线程安全问题,可能刚进了判断语句之后,执行权被剥夺了又创建好了对象,
    			//所以判断及创建对象必须是原子操作
    			synchronized (Singleton4.class) {
    				if (INSTANCE == null) {
    					//用来模拟多线程被剥夺执行权
    					try {
    						Thread.sleep(1000);
    					} catch (InterruptedException e) {
    						e.printStackTrace();
    					}
    					//如果这个地方不加volatile,会出现的问题是,指令重排 1,2,3是正常的,
    					//会重排成1,3,2 然后别的线程去拿的时候,判断为非空,但是实际上运行的时候,发现里面的数据是空的
    
    					//1 memory = allocate();//分配对象空间
    					//2 instance(memory); //初始化对象
    					//3 instance = memory; //设置instance指向刚刚分配的位置
    					INSTANCE = new Singleton4();
    				}
    			}
    		}
    		return INSTANCE;
    	}
    }
    

使用静态内部类 #

public class Singleton6 {
    private Singleton6(){

    }
    private static class Inner{
        private static final Singleton6 INSTANCE=new Singleton6();
    }
    public static Singleton6 getInstance(){
        return Inner.INSTANCE;
    }
}
  • 只有当内部类被加载和初始化的时候,才会创建INSTANCE实例对象
  • 静态内部类不会自动随着外部类的加载和初始化而初始化,他需要单独去加载和初始化
  • 又由于他是在内部类加载和初始化时,创建的,属于类加载器处理的,所以是线程安全的

自增变量

题目 #

		int i=1;
		i=i++;
		int j=i++;
		int k = i+ ++i * i++;
		System.out.println("i="+i);
		System.out.println("j="+j);
		System.out.println("k="+k);

讲解 #

对于操作数栈和局部变量表的理解 #

  • 对于下面的代码

    		int i=10;
    		int j=9;
    		j=i;
    

    反编译之后,查看字节码

    0 bipush 10
    2 istore_1
    3 bipush 9
    5 istore_2
    6 iload_1
    7 istore_2
    8 return
    

    如下图,这三行代码,是依次把10,9先放到局部变量表的1,2位置。
    之后呢,再把局部变量表中1位置的值,放入操作数栈中
    最后,将操作数栈弹出一个数(10),将数值赋给局部变量表中的位置2

    ly-20241212141840576 ly-20241212141840888 如上图,当方法为静态方法时,局部变量表0位置存储的是实参第1个数

    (当方法为非静态方法时,局部变量表0位置存储的是this引用)

  • 对于下面这段代码

    		int i=10;
    		int j=20;
    		i=i++;
    		j=++j;
    		System.out.println(i);
    		System.out.println(j);
    

    编译后的字节码

    ...

19-26_git_尚硅谷

介绍 #

使用代码托管中心(远程服务器) ly-20241212142124425

  • 团队内写作 push–clone–push— –pull ly-20241212142124625
  • 跨团队写作 fork(到自己的远程库)—clone ly-20241212142124707

创建远程库&创建别名 #

  • 官网:https://github.com
  • 现在yuebuqun注册一个账号 创建一个远程库git-demo,创建成功 ly-20241212142124787
  • 创建远程库别名 git remote -v (查看别名) 为远程库创建别名 git remote add git-demo https://github.com/lwmfjc/git-demo.git 别名创建成功 fetch和push都可以使用别名 ly-20241212142124868

推送本地库到远程库 #

  • 推送master分支 切换git checkout master
  • 推送 git push git-demo master ly-20241212142124947

拉取远程库到本地库 #

  • git pull git-demo master 结果 ly-20241212142125028

克隆远程库到本地 #

  • git clone xxxxxxx/git-demo.git ly-20241212142125111 clone之后有默认的别名,且已经初始化了本地库

团队内写作 #

  • lhc修改了git-demo下的hello.txt
  • 之后进行git add hello.txt
  • git commit -m “lhc-commit " hello.txt
  • 现在进行push git push origin master 出错了 ly-20241212142125194
  • 使用ybq,对库进行设置,管理成员 ly-20241212142125275
  • 添加成员即可 输入账号名 ly-20241212142125354
  • 将邀请函 ly-20241212142125433 发送给lhc ly-20241212142125516
  • 现在再次推送,则推送成功 ly-20241212142125598

团队外合作 #

  • 先把别人的项目fork下来 ly-20241212142125682

    ...

09-18_git_尚硅谷

命令 #

  1. 命令-设置用户签名

    • 查看 git config user.name git config user.email
    • 设置 git config --global user.name ly001 git config --global user.email xxx@xx.com
    • git的配置文件查看 ly-20241212142119718
    • 作用:区分不同操作者身份,跟后面登陆的账号没有关系
  2. 初始化本地库

    • git init

      ly-20241212142119957 多出一个文件夹 ly-20241212142120049

  3. 查看本地库状态

    • git status ly-20241212142120138 默认在master分支
    • 新增一个文件 vim hello.txt ly-20241212142120222
    • 此时查看本地库的状态 ly-20241212142120310 untracketd files 未被追踪的文件,也就是这个文件还在工作区
  4. 添加暂存区

    • git add hello.txt ly-20241212142120403 LF 将会被替换成 CRLF,windows里面是CRLF,也就是说

      这个换行符自动转换会把自动把你代码里 与你当前操作系统不相同的换行的方式 转换成当前系统的换行方式(即LFCRLF 之间的转换)

      这是因为这个hello.txt是使用vm hello.txt在git bash里面添加的,如果直接在windows文件管理器添加一个文件(hello2.txt),就会发现没有这个警告,因为他已经是CRLF了 (为了和视频保持一致,git rm –cached hello2.txt 后删除这个文件) ly-20241212142120487

    • 查看当前状态,绿色表示git已经追踪到了这个文件

      文件已经存在于暂存区 ly-20241212142120577

      ...