写了一段时间java,回顾下基础知识
C1
在命令行运行Java
javac filename.java
java filename
基本数据类型
基本类型是Java语言里的一种内置的特殊数据类型,并不是某个类的对象。 直接使用,如
char a = 'x';
这里x就是字面值
四种整型
byte 8位 2^8 -128~127
short 16 位
int 32位
long 64位 整型字面值以l结尾就是long,否则是int
默认是十进制,十六进制对应0x 二进制对应0b 八进制对于00
两种浮点型
float 32位
double 64位
浮点型字面值默认是double,后加f就是float。
以上六种默认值皆为0或0.0,均为带符号数。
一种字符型
- char 只能保存一个字符,用单引号引起,长度为16位
char a='c';++a
a 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();
流程控制
基本的三种循环
switch,在java中,可以是四种整型,String,char和enum,语法如下
switch(x){
case xx:
;
break;
case xx:
;
;
break;
}
数组
在java中,数组是固定长度的,包含了相同类型数据的==容器==
声明
声明
int a[]
引用
new int[5]
赋值 声明引用后可赋值,也可以
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
Arrays.sort
Arrays.toString
Arrays.binarySearch
Arrays.equals
Arrays.fill
其他待补充
类与对象
创建与引用
new Class();是创建
Class c1= new Class();//左边是引用
Class c2
c2=c1;//更改引用
变量的修饰符四种
先搞清类之间的关系,同包关系,子类关系,笛卡尔乘,得到
同包子类
同包非子类
不同包子类
没关系,不同包非子类
之后,四种修饰符及用处如下
private 只有自己能访问
缺省 同一个包里的可以访问或者继承,否则不行
protect 同一个包里的或者是子类,则可以继承
public 谁都可以
初始化
对象属性初始化有3种
声明该属性的时候初始化
构造方法中初始化
初始化块
类属性初始化有2种
声明该属性的时候初始化
静态初始化块
static{
itemCapacity = 6;//静态初始化块 初始化
}
singleton 单例模式
构造方法私有化
静态属性指向实例
public static的 getInstance方法,返回第二步的静态属性
饿汉模式、懒汉模式还有许多其他模式 待补充
枚举类型 enum
public enum Season{
Spring,Summer,Fall,Winter
}
for(Season s:Season.values())
System.out.println(s);
接口与抽象
instance of 判断对象是否是子类
多态的实现 需要向上类型转换和override 而隐藏就是静态方法(类方法)的重写(覆盖)
构造方法 子类一定会调用父类的构造方法,如果父类没有构造方法(确切的说,任何一个类没有显示声明构造方法),那么编译器自动加上无参无内容的空父类构造方法,如果父类生命了无参构造方法,那么子类直接调用,如果父类只声明了有参构造方法,那么子类
A:在父类中加一个无参构造方法 B:通过使用super关键字去显示的调用父类的带参构造方法==(需要放在子类的构造函数的第一句)==C:子类通过this去调用本类的其他构造方法
==所有对象==都继承自Object对象,因此也继承了其一些方法如 String toString(),boolean equals(),wait(),notify(),notifyall(),hashcode()等方法。
抽象类 一旦一个类中有抽象方法(public abstract void xx();)这个类就称为抽象类,必须加上abstract,也因此,抽象类中不一定必须全是抽象方法,也可以有实体方法,叫做==默认方法==,interface中也是这样。但是interface中的方法一定是默认为public static final,while抽象类中这三个属性都是可选的(抽象方法和实体方法都如此)
默认方法可以写在接口和抽象类中,通过default声明(否则在接口中会报错)
数字与字符串
装箱与拆箱 继承自抽象类Number的所有类都对应一个基本类型(六种数字)
转换到字符串 基本类型,使用
String.valueof(i);
装箱类型,直接使用继承自Object的to String();
转换到其他类型 调用其他类型的封装类的静态方法
xx.parsexx(String str);
数学方法
round(); MATH.PI;MATH.E,sqrt();
Character 对应char的类 有isdigit 和isnumber方法用于判断是否位数字或是否为字符串(联想 也可以通过=判断,还可通过正则简化枚举)
格式化输出。
//总长度是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"。
字符串处理函数
charAt 获取给定字符的位置
toCharArray 转换成字符数组
subString 截取子串
split 分隔开
trim 去掉空格
toLowerCase toUpperCase 转换成大小写
indexOf lastIndexOf contains 字符串的位置,是否包含
replaceAll replaceFirst 字符串替换
字符串比较
== 判断是否为同一个对象
equals(String str)
判断是否一样startsWith
以...开始endsWith
以...结束
Stringbuffer 变长字符串
append delete insert reverse 以及length capacity 这些属性 性能较高
日期处理
Date d=new Date();可选参数 int 表示从1970年1月1日过了x毫秒(10^-3秒)对应的时间。
gettime返回long类型,表示从那时到现在经历了多少毫秒
System.currentTimeMillis(); 返回系统当前时间,long型。 日历类型:
//通过日历对象得到日期对象
Date d = c.getTime();
Date d2 = new Date(0);
c.setTime(d2); //把这个日历,调成日期 : 1970.1.1 08:00:00
还有add和set方法。
- 通过这种方式来进行格式化输出
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
异常处理
通过try catch finally的方式进行处理。
异常可分为错误ERROR和Exception,后者又分为运行时异常(runtimeException)和可查异常,三者都继承了Throwable类
[图片上传失败...(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是一个地位的,不过不是字节而是字符读取。
集合框架
用处:变长数组,可以同时存放多种类型,如各种类和基本类型。
ArrayList ar=new Arraylist();
,然后调用add();remove();get();set();等方法即可对其操作,它会自动变长度。还可以指定其可以增加的类型,如
ArrayList
(后边一个<>中指定的类型在SDK7后可以省略。这样,就保证只能add这种类型。此外,通过实现统一个接口,可以实现只能增加确定数目个类。lh=new ArrayList <>(); ArrayList实际上是实现了List接口==待补充==,继承了其所有方法。
ArrayList的遍历,可以通过for(;;)get(index)来取得;也可以通过
Iterator
这样来获得迭代器,然后通过it =lh.iterator(); it.hasnext()和it.next()
进行遍历,还可以通过foreach循环进行遍历。只是在要删除其中某些元素时,直接使用it或者foreach会出现错误==待补充==(为什么?),,可以放到一个临时容器,删除临时容器来实现对象(内存空间的释放),这样在原List(可以发现其为链表结构)中依然是连续的。LinkedList相比ArrayList实现了队列、双向链表的接口,因此多了addlast();addfirst();以及offer() 在最后添加元素;poll() 取出第一个元素;peek() 查看第一个元素这些方法
二叉树排序、遍历算法java实现时应注意的事情,如travel方法应独立于对象==待补充==
HashMap和HashSet,后者封装了前者,都是无序的
Collection是一个接口,List,Deque(间接继承),Set都继承了它,但Map是存放
的,故无关。Collections是一个类,容器的工具类,就如同Arrays是数组的工具类 HashMap和Hashtable都实现了Map接口,都是键值对保存数据的方式区别1: HashMap可以存放null;Hashtable不能存放null区别2:HashMap不是线程安全的类;Hashtable是线程安全的类
HashSet: 无序;LinkedHashSet: 按照插入顺序;TreeSet: 从小到大排序
泛型generic
之前用过的,集合中的泛型
ArrayList
集合中实现了某个接口使得可以直接<>这样写。相对的:lh=new ArrayList <>() public class Mystack
自己声明{}``Mystack mh=new Mystack<>(); 即可,其中T将会替换掉类中方法返回值、参数类型和属性的类型声明(可以)。 如果希望只取出,不插入,就使用? extends Hero;如果希望只插入,不取出,就使用? super Hero;如果希望,又能插入,又能取出,就不要用通配符?
子类泛型和父类泛型都不能相互转换,原因是子类转父类(若是对象则可以)再加新的,内存格式不对(其他子类),父类转子类,倒是可以加进去,但是取出会有问题。根本原因是相比对象,集合泛型同时具备了加入和取出操作,是双向的,而向上类型转换是单向的,导致转换无法进行。
Lambda表达式
大体来说,有三种类型来写一段代码实现一个功能,一是直接刚代码,但耦合性大,不易修改,不好复用,二是通过类解耦出来,通过匿名类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.IllegalMonitorStateException
google得知,是因为调用.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包的方式。
待续 ......