Java基础

写了一段时间java,回顾下基础知识

C1

在命令行运行Java


javac filename.java

java filename

基本数据类型

基本类型是Java语言里的一种内置的特殊数据类型,并不是某个类的对象。 直接使用,如


char a = 'x';

这里x就是字面值

四种整型
  1. byte 8位 2^8 -128~127

  2. short 16 位

  3. int 32位

  4. long 64位 整型字面值以l结尾就是long,否则是int

默认是十进制,十六进制对应0x 二进制对应0b 八进制对于00

两种浮点型
  1. float 32位

  2. double 64位

浮点型字面值默认是double,后加f就是float。

以上六种默认值皆为0或0.0,均为带符号数。

一种字符型
  1. char 只能保存一个字符,用单引号引起,长度为16位

char a='c';++aa is b;

一种字符串型

String str="xxx";

严格来说,字符串是Immutable的

类型转换

强制类型转换用于从大到小

不同类型数据进行运算取较大数的

如果任何运算单元的长度都不超过int,那么运算结果就按照int来计算 (如两个byte相加,结果一定是int)

命名规则

变量命名只能使用字母 数字 $ _ (中文也可以但我不会这么干的)

final

当一个变量被final修饰的时候,该变量只有一次赋值的机会

操作符

逻辑操作符

注意java中逻辑操作符的操作数是true和false 不会是别的

短& 长&& 短| 长|| 非!

位操作符

有~ & | ^

三元操作符

? :

用于获得输入

Scanner s= new Scanner;

s.nextint();s.nextline();s.nextfloat();

因为回车也算是字符串的一部分,刚好也是字符串(line)结束的标志,故在回撤之后读取字符串需要额外加一行s.nextline();

流程控制

  1. 基本的三种循环

  2. switch,在java中,可以是四种整型,String,char和enum,语法如下


switch(x){

case xx:

;

break;

case xx:

;

;

break;

}

数组

在java中,数组是固定长度的,包含了相同类型数据的==容器==

声明
  1. 声明 int a[]

  2. 引用 new int[5]

  3. 赋值 声明引用后可赋值,也可以 int a[]=new int[5]{1,2,3,4,5}orint a[]=new int[]{1,2,3}orint a []={1,2,3}

增强型for 循环

for (int each:a) xxx;

工具类Arrays
  1. Arrays.sort

  2. Arrays.toString

  3. Arrays.binarySearch

  4. Arrays.equals

  5. Arrays.fill

其他待补充

类与对象

创建与引用

new Class();是创建

Class c1= new Class();//左边是引用

Class c2

c2=c1;//更改引用

变量的修饰符四种

先搞清类之间的关系,同包关系,子类关系,笛卡尔乘,得到

  1. 同包子类

  2. 同包非子类

  3. 不同包子类

  4. 没关系,不同包非子类

之后,四种修饰符及用处如下

  1. private 只有自己能访问

  2. 缺省 同一个包里的可以访问或者继承,否则不行

  3. protect 同一个包里的或者是子类,则可以继承

  4. public 谁都可以

初始化

对象属性初始化有3种

  1. 声明该属性的时候初始化

  2. 构造方法中初始化

  3. 初始化块

类属性初始化有2种

  1. 声明该属性的时候初始化

  2. 静态初始化块


static{

        itemCapacity = 6;//静态初始化块 初始化

    }

singleton 单例模式
  1. 构造方法私有化

  2. 静态属性指向实例

  3. public static的 getInstance方法,返回第二步的静态属性

饿汉模式、懒汉模式还有许多其他模式 待补充

枚举类型 enum

public enum Season{

    Spring,Summer,Fall,Winter

    }

  for(Season s:Season.values())

            System.out.println(s);

接口与抽象

  1. instance of 判断对象是否是子类

  2. 多态的实现 需要向上类型转换和override 而隐藏就是静态方法(类方法)的重写(覆盖)

  3. 构造方法 子类一定会调用父类的构造方法,如果父类没有构造方法(确切的说,任何一个类没有显示声明构造方法),那么编译器自动加上无参无内容的空父类构造方法,如果父类生命了无参构造方法,那么子类直接调用,如果父类只声明了有参构造方法,那么子类

  4. A:在父类中加一个无参构造方法 B:通过使用super关键字去显示的调用父类的带参构造方法==(需要放在子类的构造函数的第一句)==C:子类通过this去调用本类的其他构造方法

  5. ==所有对象==都继承自Object对象,因此也继承了其一些方法如 String toString(),boolean equals(),wait(),notify(),notifyall(),hashcode()等方法。

  6. 抽象类 一旦一个类中有抽象方法(public abstract void xx();)这个类就称为抽象类,必须加上abstract,也因此,抽象类中不一定必须全是抽象方法,也可以有实体方法,叫做==默认方法==,interface中也是这样。但是interface中的方法一定是默认为public static final,while抽象类中这三个属性都是可选的(抽象方法和实体方法都如此)

  7. 默认方法可以写在接口和抽象类中,通过default声明(否则在接口中会报错)

数字与字符串

  1. 装箱与拆箱 继承自抽象类Number的所有类都对应一个基本类型(六种数字)

  2. 转换到字符串 基本类型,使用String.valueof(i); 装箱类型,直接使用继承自Object的to String();

  3. 转换到其他类型 调用其他类型的封装类的静态方法xx.parsexx(String str);

  4. 数学方法 round(); MATH.PI;MATH.E,sqrt();

  5. Character 对应char的类 有isdigit 和isnumber方法用于判断是否位数字或是否为字符串(联想 也可以通过=判断,还可通过正则简化枚举)

  6. 格式化输出。


        //总长度是8,左对齐

        System.out.format("%-8d%n",year);

        //总长度是8,不够补0

        System.out.format("%08d%n",year);

        //千位分隔符

        System.out.format("%,8d%n",year*10000);

本质上是printf(1)在DOS和Windows中,每行结尾是 “\r\n”;(2)Linux系统里,每行结尾只有 “\n”;(3)Mac系统里,每行结尾是只有 "\r"。

字符串处理函数
  1. charAt 获取给定字符的位置

  2. toCharArray 转换成字符数组

  3. subString 截取子串

  4. split 分隔开

  5. trim 去掉空格

  6. toLowerCase toUpperCase 转换成大小写

  7. indexOf lastIndexOf contains 字符串的位置,是否包含

  8. replaceAll replaceFirst 字符串替换

字符串比较
  1. == 判断是否为同一个对象

  2. equals(String str)判断是否一样

  3. startsWith以...开始 endsWith以...结束

Stringbuffer 变长字符串

append delete insert reverse 以及length capacity 这些属性 性能较高

日期处理

  1. Date d=new Date();可选参数 int 表示从1970年1月1日过了x毫秒(10^-3秒)对应的时间。

  2. gettime返回long类型,表示从那时到现在经历了多少毫秒

  3. System.currentTimeMillis(); 返回系统当前时间,long型。 日历类型:


        //通过日历对象得到日期对象

        Date d = c.getTime();

        Date d2 = new Date(0);

        c.setTime(d2); //把这个日历,调成日期 : 1970.1.1 08:00:00

还有add和set方法。

  1. 通过这种方式来进行格式化输出

M 代表月

d 代表日

H 代表24进制的小时

h 代表12进制的小时

m 代表分钟

s 代表秒

S 代表毫秒

SimpleDateFormat sdf=new SimpleDateFormat("y-M-dd");

        System.out.println(sdf.format(new Date(System.currentTimeMillis())));

C2

异常处理

  1. 通过try catch finally的方式进行处理。

  2. 异常可分为错误ERROR和Exception,后者又分为运行时异常(runtimeException)和可查异常,三者都继承了Throwable类

  3. [图片上传失败...(image-5b2c1d-1551662952330)]

文件操作


File f = new File("");

        // 以字符串数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)

        f.list();

        // 以文件数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)

        File[]fs= f.listFiles();

        // 以字符串形式返回获取所在文件夹

        f.getParent();

        // 以文件形式返回获取所在文件夹

        f.getParentFile();

        // 创建文件夹,如果父文件夹skin不存在,创建就无效

        f.mkdir();

        // 创建文件夹,如果父文件夹skin不存在,就会创建父文件夹

        f.mkdirs();

        // 创建一个空文件,如果父文件夹skin不存在,就会抛出异常

        f.createNewFile();

        // 所以创建一个空文件之前,通常都会创建父目录

        f.getParentFile().mkdirs();

        // 列出所有的盘符c: d: e: 等等

        f.listRoots();

        // 刪除文件

        f.delete();

        // JVM结束的时候,刪除文件,常用于临时文件的删除

        f.deleteOnExit();

        File f = new File("d:/LOLFolder/LOL.exe");

        System.out.println("当前文件是:" +f);

        //文件是否存在

        System.out.println("判断是否存在:"+f.exists());

        //是否是文件夹

        System.out.println("判断是否是文件夹:"+f.isDirectory());

        //是否是文件(非文件夹)

        System.out.println("判断是否是文件:"+f.isFile());

        //文件长度

        System.out.println("获取文件的长度:"+f.length());

        //文件最后修改时间

        long time = f.lastModified();

        Date d = new Date(time);

        System.out.println("获取文件的最后修改时间:"+d);

        //设置文件修改时间为1970.1.1 08:00:00

        f.setLastModified(0);

        //文件重命名

        File f2 =new File("d:/LOLFolder/DOTA.exe");

        f.renameTo(f2);

        System.out.println("把LOL.exe改名成了DOTA.exe");

          System.out.println("f1的绝对路径:" + f1.getAbsolutePath());

        // 相对路径,相对于工作目录,如果在eclipse中,就是项目目录

        File f2 = new File("LOL.exe");

        System.out.println("f2的绝对路径:" + f2.getAbsolutePath());

        // 把f1作为父目录创建文件对象

        File f3 = new File(f1, "LOL.exe");

流是传输数据的一个方式,从程序的角度来区分输入流和输出流,来源可以是硬盘、程序、网络等任意地方

FileInputStream fis= new FileInputStream(new File(str));

InputStream和OutputStream是最基本的字节流,文件输入输出流继承了他们。

关闭流有三种不同的编写位置,可以在try中关闭,但可能因为在fis.close执行前就抛出异常导致无法关闭,也可以把close定义在finally中,但声明xx=null就要在catch前,因为try语句块和finally语句块不共享变量,最后一种方法是sdk7开始的try后的()声明和初始化流,这样在try结束的时候就自动关闭了==待补充==

Reader Writer和刚才的InputStream OutputStream是一个地位的,不过不是字节而是字符读取。

集合框架

  1. 用处:变长数组,可以同时存放多种类型,如各种类和基本类型。ArrayList ar=new Arraylist();,然后调用add();remove();get();set();等方法即可对其操作,它会自动变长度。

  2. 还可以指定其可以增加的类型,如ArrayList lh=new ArrayList <>();(后边一个<>中指定的类型在SDK7后可以省略。这样,就保证只能add这种类型。此外,通过实现统一个接口,可以实现只能增加确定数目个类。

  3. ArrayList实际上是实现了List接口==待补充==,继承了其所有方法。

  4. ArrayList的遍历,可以通过for(;;)get(index)来取得;也可以通过 Iterator it =lh.iterator();这样来获得迭代器,然后通过it.hasnext()和it.next()进行遍历,还可以通过foreach循环进行遍历。只是在要删除其中某些元素时,直接使用it或者foreach会出现错误==待补充==(为什么?),,可以放到一个临时容器,删除临时容器来实现对象(内存空间的释放),这样在原List(可以发现其为链表结构)中依然是连续的。

  5. LinkedList相比ArrayList实现了队列、双向链表的接口,因此多了addlast();addfirst();以及offer() 在最后添加元素;poll() 取出第一个元素;peek() 查看第一个元素这些方法

  6. 二叉树排序、遍历算法java实现时应注意的事情,如travel方法应独立于对象==待补充==

  7. HashMap和HashSet,后者封装了前者,都是无序的

  8. Collection是一个接口,List,Deque(间接继承),Set都继承了它,但Map是存放的,故无关。Collections是一个类,容器的工具类,就如同Arrays是数组的工具类

  9. HashMap和Hashtable都实现了Map接口,都是键值对保存数据的方式区别1: HashMap可以存放null;Hashtable不能存放null区别2:HashMap不是线程安全的类;Hashtable是线程安全的类

  10. HashSet: 无序;LinkedHashSet: 按照插入顺序;TreeSet: 从小到大排序

泛型generic

  1. 之前用过的,集合中的泛型 ArrayList lh=new ArrayList <>()集合中实现了某个接口使得可以直接<>这样写。相对的:public class Mystack{}``Mystack mh=new Mystack<>(); 自己声明即可,其中T将会替换掉类中方法返回值、参数类型和属性的类型声明(可以)。

  2. 如果希望只取出,不插入,就使用? extends Hero;如果希望只插入,不取出,就使用? super Hero;如果希望,又能插入,又能取出,就不要用通配符?

  3. 子类泛型和父类泛型都不能相互转换,原因是子类转父类(若是对象则可以)再加新的,内存格式不对(其他子类),父类转子类,倒是可以加进去,但是取出会有问题。根本原因是相比对象,集合泛型同时具备了加入和取出操作,是双向的,而向上类型转换是单向的,导致转换无法进行。

Lambda表达式

大体来说,有三种类型来写一段代码实现一个功能,一是直接刚代码,但耦合性大,不易修改,不好复用,二是通过类解耦出来,通过匿名类new一个接口Comparator c = new Comparator() {}(集合的排序方法,使用的泛型,一般可以没有)在其中重载接口中的方法,并在另一个方法中和它要处理的数据一起作为参数传入方法,但这样会生成很多匿名类。三是直接把数据和方法传入,那个方法就是Lambda表达式,格式是(argu)->(expression).使用Lambda表达式的好处是简洁,坏处是长了不易读,不易维护,不易输出日志。

多线程

多线程是指一个进程中多个同时进行的任务==待补充==,java中多线程实现方式由继承thread类:class A extends Thread;A aa=new A();A.start()(需要在A中重写run()方法);实现runnable接口class B implements Runnable;B bb=new B;new Thread(B).start(),Runnable中声明了run()方法,自然B中需要重载,但因为R中没有start方法,只有Thread中有start()方法,故需要通过Thread的构造函数来进行启动线程。匿名类也可以实现多线程。Thread t1=new Thread(){run(){};};t1.start();匿名类的一个好处是可以很方便的访问外部的局部变量。前提是外部的局部变量需要被声明为final。(JDK7以后就不需要了)。还能使得代码简洁。

线程的实现作为一个类或者接口肯定有一些方法,包括yield暂停,sleep()睡眠,setpriority设置优先级,setDaemon(true);设置为守护线程

除了这些方法,多个线程对一个资源进行操作就会出现问题,根本原因是一个Thread中操作非原子,可再分,因而处理器执行A的一部分再去执行B,就会造成数据出错(脏数据?)。的最简单的:银行存款问题。

那么,如何解决?根据刚才说的,只需要一个线程占有资源的时候,其他资源不能同时访问进而占有进程即可。可以通过java关键字sy来实现synchronized


Object someObject =new Object();

synchronized (someObject){

  //此处的代码只有占有了someObject后才可以执行

}

这里someobject就相当于一个信号量,也就是说,synchronized保证同时只有一个线程占有代码块中的操作。 因此,也可以用继承了THread类的类作为Sy方法的参数,this(在方法声明)或者对象名(在代码块中)都可以。 进一步地,直接在类的定义的方法前加上sy,表示这个方法在被调用时所属对象被锁定(和上文的sy(this)是一样的。

与syn类似的lock()

Lock lock = new ReentrantLock();这个lock也可以完成syn所能完成的任务,只是syn在代码块结束后自动释放,而lock必须手动释放,因此若忘记释放,就会造成死锁;另一个区别是lock可以试图在一定时间内占用,如果占用失败,就拉到,这可以很大程度上减少死锁的产生。

相对应syn的交互办法,lock也有相应的交互办法,分别是await、signal、signalall,这些方法从lock对象得到的condition那里得到。

此外,syn是java中的关键字,而lock是一个接口,是代码层面的实现。

线程安全的类

如果一个类,其方法都是有synchronized修饰的,那么该类就叫做线程安全的类。同一时间,只有一个线程能够进入 这种类的一个实例 的去修改数据,进而保证了这个实例中的数据的安全(不会同时被多线程修改而变成脏数据)。java本身实现了一些线程安全的类。==待补充==

死锁

死锁是在线程安全的基础上产生的,如t1占有r1,之后想要占有r2,whilet2占有r2,之后想要占有r1,此时它俩互相等待,进入死锁。扩展到更多的线程和资源的关系中,判定方法见操作系统(总算操作系统没白学)==待补充==

如何解决死锁?==待补充==

线程交互

多个线程同时进行,肯定需要交互,可以通过网络监听+switch。也有一些方法如wait()和notify(),属于Object上的方法,前者使当前线程停下等待后者,后者使得因前者停下的线程继续运行。

生产者消费者问题

一开始我自己写的时候,是在生产者和消费者这两个类中实现了notify()和wait(),然后在wait那里报错,一开始写的还是this.wait().....后来改成了ms.wait(),报错是java.lang.IllegalMonitorStateExceptiongoogle得知,是因为调用.wait()的方法没有+同步锁, 即syn,因此,wait()需要放在资源(的方法)中,而非调用资源的线程中!根本原因是this.wait()表示 让占有this的线程等待,并临时释放占有。this.notify() 表示通知那些等待在this的线程,可以苏醒过来了。 等待在this的线程,恰恰就是减血线程。 一旦recover()结束, 加血线程释放了this,减血线程,就可以重新占有this,并执行后面的减血工作。

线程池

类似刚才的生产者消费者问题,线程池的资源是指一个个需要解决的问题,而消费者是线程池中的线程,生产者是....emmm添加任务的人。

`ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue());

`然后 通过excute(Runnable x)来添加任务。

atomic operation

详见os,不可再分的操作,java中提供了 AtomicInteger at= new AtomicInteger(3);来保证++i的原子性操作(本来只有这种int i=1;是原子操作的)。

网络编程部分

小的聊天工具

Client.java&Server.java聊天,傻到每个.java建立一个监听和一个发送,这样一个开启监听的时候也开启也发送,这个发送永远了到不了另一个监听,因为另一个监听还没开(试想你原来的Client先开的话,Server是接受不到消息的)。所以,只需要一个Socket连接。然后把接收和发送消息写到同一个while(true)里,此时idea会报错:循环不会结束,后边的访问不到,这时业务逻辑稍微改一下不===true就好。但是while()这样有一个问题,就是必须是一问一答的形式,因为一定会是{接收消息;发送消息;}的样子。此时需要多线程(两个就够了)。

网络基础知识部分

java可以监听一个ip的一个端口来接收消息,至于具体怎样,去看计算机网络吧==待补充==

C3

反射Reflection

类对象,就是用于描述这种类,都有什么属性,什么方法的。对比Hero对象,Animal对象。

获取类对象有三个方法,分别是类名.class;对象名.getclass;以及Class.forname(类名);

之后通过类对象的构造器和强制类型转换获得想要的对象


Constructor c=b.getConstructor();

Hero hh=(Hero)c.newInstance();

除了第一个,其他都会初始化要求类对象的类的类方法即static语句块。

与反射有关的,syn锁+在对象方法上,占用的对象是当前对象,也即当前对象无法被其他对象访问,而+在类方法之前,占用的对象就是类对象。

通过反射获取对象其内容是空的,要通过获取到的唯一的类对象的方法进行设定,具体如下


Field f1=hh.getClass().getField("name");

Field f2=hh.getClass().getDeclaredField("hp");

f1.set(hh,"temmo");

区别是前者可以获得public的属性,包括从父类继承的,后者可以获得所有属性,但不能获得继承来的,同时,private的不可直接访问(protected的在同一个包里可以(本来就可以)),需要方法setAccessible(true);

获取对象之后就需要获取、调用方法,与刚才类似


Method m=hh.getClass().getDeclaredMethod("setHp", double.class);

m.invoke(hh,23.9);

那么,这样做又什么好处呢?从上边看出来,反射可以通过String类名直接获得类对象,进而获得对象并对其属性和方法进行修改。也就是说,可以通过外部配置文件决定生成的类。(如果要用java自己实现,读取文件之后做if或者switch,或者把那个String型类名嵌入到java代码中)这样就实现了解耦。Spring框架就是这么干的。

Annotation

java中注解是可以被编译器接收的对代码段起影响的代码,工程上目的主要是解耦。可以分为内置注解、框架注解和自定义注解,其中自定义注解需要元注解进行配置,内置注解也需要,都需要(......)。根据注解的作用域@Retention,注解分为RetentionPolicy.SOURCE: Java源文件上的注解;RetentionPolicy.CLASS: Class类文件上的注解;RetentionPolicy.RUNTIME: 运行时的注解。

还有自定义注解,通过配置一个@interface,并在另一个类前用其注解,然后通过反射这个类获得在注解处配置的值,还是有点迷,这样做的意义除了解耦还有啥?==待补充==

日志

通常,调试信息需要通过打印来输出,但这样有一些缺点。通过日志更加方便,在使用之前,首先要导入jar包,idea导入jar包的方式。

待续 ......

你可能感兴趣的:(Java基础)