国庆java学习

第一天 类的加载与反射

使用一个还没被加载到内存中的类,jvm会通过加载、连接、初始化来对该类初始化。

什么是类的加载呢?类的加载就是将类的class文件读入内存,并为之创建一个java.lang.Class对象,也就是说当程序使用任何类时,系统都会为之建立一个java.lang.Class对象。

类的连接:连接阶段负责把类的二进制数据合并到JRE中,分为三个步骤:
第一,验证,检验被加载的类是否有正确的内部结构;第二,准备,负责为类的类变量分配内存,并设置默认初始值。第三,解析将类的二进制数据中的符号引用替换成直接引用。

每个类被加载之后,系统就会为该类生成一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类

  1. 当你知道该类的全路径名时,使用Class类的forName(String clazzName)静态方法,如forname(“java.util.Random”)
  2. 调用某个类的class属性来获取该类对应的Class对象,如person.class,这种方法只适合在编译前就知道操作的 Class。
  3. 调用某个对象的getClass()方法,如p1.getClass()

反射测试代码:

class test {
    private int age;
    private String name;
    private int testint;
    public test(int age){
        this.age=age;
    }
    public test(int age, String name){
        this.age=age;
        this.name=name;
    }
    public test(String name){
        this.name=name;
    }
    public test(){
    }

    public static void main(String[] args) {
        test test1=new test();
        String s="java.util.Random";
        try {
            Class c1=Class.forName(s);
            System.out.println(c1.getName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Class c4=test1.getClass();
        System.out.println(c4.getPackage());//获取包名
        Constructor[] constructors;
        //getDeclaredConstructors获取类的所有构造方法
        constructors=c4.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }

        for (int i = 0; i < constructors.length; i++) {
            //getModifiers获取构造方法类型
            System.out.println(Modifier.toString(constructors[i].getModifiers()) + "参数:");
            //getParameterTypes获取构造方法的参数类型
            Class[]parametertypes=constructors[i].getParameterTypes();
            for (int j = 0; j < parametertypes.length; j++) {
                System.out.print(parametertypes[j].getName() + " ");

            }
            System.out.println("");
        }


    }
}

第二天 集合与泛型

集合

集合框架结构图
Collection集合的方法:

在这里插入图片描述
List:
(1)ArrayList:底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素
(2)LinkedList 底层数据结构是链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素
(3)Vector:底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素

Set:
(1)HashSet底层数据结构采用哈希表实现,元素无序且唯一,线程不安全,效率高,可以存储null元素,元素的唯一性是靠所存储元素类型是否重写hashCode()和equals()方法来保证的,如果没有重写这两个方法,则无法保证元素的唯一性。

(2)LinkedHashSet底层数据结构采用链表和哈希表共同实现,链表保证了元素的顺序与存储顺序一致,哈希表保证了元素的唯一性。线程不安全,效率高。

(3)TreeSet底层数据结构采用二叉树来实现,元素唯一且已经排好序;唯一性同样需要重写hashCode和equals()方法,二叉树结构保证了元素的有序性。

Map:
Map用于保存具有映射关系的数据,Map里保存着两组数据:key和value,它们都可以使任何引用类型的数据,但key不能重复。所以通过指定的key就可以取出对应的value。
注意,Map 没有继承 Collection 接口!
(1)HashMap
使用位桶和链表实现(最近的jdk1.8改用红黑树存储而非链表),它是线程不安全的Map,方法上都没有synchronize关键字修饰

(2)HashTable
hashTable是线程安全的一个map实现类,它实现线程安全的方法是在各个方法上添加了synchronize关键字。

(3)TreeMap
TreeMap也是一个很常用的map实现类,因为他具有一个很大的特点就是会对Key进行排序,使用了TreeMap存储键值对

(4)LinkedHashMap
LinkedHashMap它的特点主要在于linked,带有这个字眼的就表示底层用的是链表来进行的存储。相对于其他的无序的map实现类,还有像TreeMap这样的排序类,linkedHashMap最大的特点在于有序,但是它的有序主要体现在先进先出FIFIO上。

泛型

泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

泛型的使用
泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法



第三天 异常

一、异常

定义:Java 中的异常(Exception)又称为例外,是一个在程序执行期间发生的事件,它中断正在执行的程序的正常指令流。

在Java中,所有的异常都有一个共同的父类Throwable,该类有两个重要的子类:Exception和Error,二者都是Java异常处理的重要子类,各自都包含大量子类。它们都是java.lang下的类。

Error与Exception
Error是程序无法处理的错误,它是由JVM产生和抛出的,比如OutOfMemoryError、ThreadDeath等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。程序中应当尽可能去处理这些异常。

异常分类

运行时异常和非运行时异常

运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。
非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,要么使用try-catch捕获,要么使用throws语句抛出,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

异常的处理

声明异常:throws

捕获异常:try,catch,finally

抛出异常throw

throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结 束当前方法的执行。

throw new 异常类名(参数);

声明抛出异常throws

运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常。

修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2 ... { }


第四天 注解

Java 注解(Annotation)又称 Java 标注,是JDK5.0引入的一种注释机制。 注解是元数据的一种形式,提供有关于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。

Annotation(注解)就是Java提供了一种元程序中的元素关联任何信息和着任何元数据(metadata)的途径和方法。Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion对象,然后通过Annotion对象来获取注解里面的元数据。

java内置注解

  • @Deprecated
    意思是说此方法已过时,是因为有新的API的类替代了此方法。这个被划去的方法仍然是可以正常使用的。

  • @Override
    用于标明此方法覆盖了父类的方法。

  • @SuppressWarnings
    用于通知java编译器忽略特定的编译警告。

  • @FunctionalInterface
    指定接口必须为函数式接口,如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错。函数式接口”是指仅仅只包含一个抽象方法的接口。

  • @SafeVarargs
    这个注解用来抑制堆污染警告

元注解

在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之为 meta-annotation(元注解)。一般的,我们在定义自定义注解时,需要指定的元注解有两个 :@Target和@Retention。

java.lang.annotation 提供了四种元注解,专门注解其他的注解(在自定义注解的时候,需要使用到元注解):

@Target – 注解用于什么地方
@Retention – 什么时候使用该注解
@Documented – 注解是否将包含在JavaDoc中
@Inherited – 是否允许子类继承该注解

注解语法

注解的定义:注解通过@interface 关键字进行定义。注解的定义格式是 :public @interface 注解名 {定义体}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation {
      String value() default "";
}


第五天 IO

在这里插入图片描述
IO流的分类

· 根据数据处理的不同类型分为:字节流和字符流

· 根据数据流向不同分为:输入流和输出流

字节流和字符流的区别:

· 读写单位的不同:字节流以字节(8bit)为单位。字符流以字符为单位,根据码表映射字符,一次可能读多个字节。

· 处理对象不同:字节流可以处理任何类型的数据,如图片、avi等,而字符流只能处理字符类型的数据。

输入流和输出流

对于输入流只能进行读操作。

对于输出流只能进行鞋操作。

程序中需要对于传输数据的不同特性而使用不用的流。

File类

File类是对文件系统中文件以及文件夹进行封装的对象,可以通过对象的思想来操作文件和文件夹。File类保存文件或目录的各种数据信息,包括文件名、文件长度、最后修改时间、是否可读、获取当前文件的路径名、判断文件是否存在、获取当前目录中的文件列表、创建、删除文件和目录等方法。

对象序列化

序列化:把Java对象转换为字节序列的过程。
反序列化:把字节序列恢复为Java对象的过程。
用途
把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;(持久化对象)
在网络上传送对象的字节序列。(网络传输对象)



第六天 多线程

一些概念:

多线程:指的是这个程序(一个进程)运行时产生了不止一个线程

并行与并发:
并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。

并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。

多线程的创建方式

方式1:继承于Thread类
1.创建一个集成于Thread类的子类
2.重写Thread类的run()方法
3.创建Thread子类的对象
4.通过此对象调用start()方法

public class j6 {
    public static void main(String[] args) {
        new ThreadTest().start();
        new ThreadTest().start();
        System.out.println("main thread is running");
    }
}

class ThreadTest extends Thread{
    @Override
    public void run() {
        super.run();
        System.out.println(Thread.currentThread().getName()+" is running");

    }
}

输出的结果次序是随机的,跟每次程序的进程调度有关。

main thread is running
Thread-0 is running
Thread-1 is running

方式2:实现Runable接口方式
1.创建一个实现了Runable接口的类
2.实现类去实现Runnable中的抽象方法:run()
3.创建实现类的对象
4.将此对象作为参数传递到Thread类中的构造器中,创建Thread类的对象
5.通过Thread类的对象调用start()

//通过实现Runnable接口创建线程
class RunnableTest implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running");
    }
}

class TestRunnable{
    public static void main(String[] args) {
        RunnableTest t=new RunnableTest();
        Thread oneThread=new Thread(t);
        oneThread.start();
    }
}

结果

Thread-0 is running

比较创建线程的两种方式:
开发中,优先选择实现Runable接口的方式
原因1:实现的方式没有类的单继承性的局限性
2:实现的方式更适合用来处理多个线程有共享数据的情况
联系:Thread也是实现自Runable,两种方式都需要重写run()方法,将线程要执行的逻辑声明在run中

线程的死锁问题
线程死锁的理解:僵持,谁都不放手,一双筷子,我一只你一只,都等对方放手(死锁,两者都进入阻塞,谁都吃不了饭,进行不了下面吃饭的操作)
出现死锁以后,不会出现提示,只是所有线程都处于阻塞状态,无法继续

死锁的解决办法:
1.减少同步共享变量
2.采用专门的算法,多个线程之间规定先后执行的顺序,规避死锁问题
3.减少锁的嵌套。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值