在这里插入代码片
# Java学习笔记
博客仅用于记录学习Java过程遇到的问题
初遇Java
1.Java语言特点
- Simple
Java语言简单,它的编译器和解释器所占空间内存不到250KB - Object-oriented(面向对象)
Java是纯面向对象的语言 - Distributed(分布式)
Java语言在操作和数据上分布,但在逻辑上统一。数据分布是指数据分散存放在网络上的不同主机中,从而解决了数据量大的问题。操作分布是指将计算分布到不同的主机上进行处理 - High-performance(高性能)
Java字节码转换为机器码非常方便。 - Multi-threaded(多线程)
- Dynamic(动态)
Java在执行中可以动态加载各种类库 - Robust and secure(健壮,安全)
Java没有c++的指针类型,从而避免了利用指针恶意篡改不属于程序的内存空间,除此之外,Java还具有字节码校验器、运行时间内存分布、文件访问限制、和类装载器,提高了安全性 - Architecture-netutral(平台无关性)
- Portable(可移植性)
- Interpreted(解释性)
2.Java语言跨平台实现的原理是什么?
程序运行的操作系统可以是Windows/Linux/UNIX… 一般情况下在一种操作系统能运行的程序,换个操作系统就无法运行,需要重新编写,而Java却能解决这个问题。
平台指的是操作系统+CPU,CPU的种类有多种,此外不同CPU支持的指令集有可能相同也有可能不同,指令集是CPU用来计算和控制计算机系统的指令的集合。不同的操作系统支持不同的CPU,实际上是不同的指令集合。
*.c或.cpp程序不可跨平台原因
编译器将.c或.cpp文件便以为可执行的代码,由于这里的编译器是与平台相关的,因此可执行代码也与平台相关,导致程序不可跨平台运行。
Java语言跨平台使用原因
在C、C++中可执行代码与平台相关导致语言不能跨平台使用,在Java语言中,与平台无关的编译器编译源程序,生成与平台无关的中间码.class,再由与平台对应的虚拟机(JVM),编译为特定平台下的机械码然后运行,不同的平台有相对应的虚拟机。
3.JDK,JRE,JVM有什么区别?
JDK是 Java 语言的软件开发工具包。JDK的安装目录下有一个jre目录,里面有两个文件夹bin和lib,bin里的是jvm,lib中则是jvm工作所需要的类库,而jvm和 lib合起来就称为jre。
Java语言基础
1.Java数据类型
Java数据类型包括基本数据类型和复合数据类型。
基本数据类型 | 复合数据类型 |
---|---|
整数类型:byte,short,int,long; 浮点类型:fioat,double; 字符类型:char; 布尔类型:boolean | class; interface(接口) |
2.基本数据类型转换
自动类型转换:不同的基本数据类型进行混合运算时,不同的数据类型先转换为同种数据类型(低级数据类型转换为高级数据类型),再进行运算。
强制类型转换:高级数据类型转换为低级数据类型。
3.为什么需要包装类?
Java是纯面向对象的语言,但它的基本数据类型却不是对象,导致基本数据类型在一些场合不能直接使用,比如转型,为了解决这个问题Java语言为每个基本数据类型设计了一个对应的类。
4.基本数据类型与对应的包装类有什么区别?它们之间的相互转换又是怎样的?
以int和Integer为例
int | Integer | |
---|---|---|
存储 | 栈 | 堆 |
初始值 | 0 | null |
方法中的传递 | 值传递 | 引用传递 |
是否能放入LinkedList和ArrayList | 不能 | 能 |
不是对象,没有方法和字段 | 是对象、具有方法和字段 |
int转Integer(基本数据类型转换为包装类)(装箱):
int i=1;
//手动装箱
Integer a=new Integer(i);
//自动装箱
Integer b=i;
Integer转int(包装类转基本数据类型)(拆箱):
Integer x=new Integer(1);
//手动拆箱
int i=x.intValue();
//自动拆箱
int k=x;
5.逻辑运算符&与&&区别、逻辑运算符&与位运算符&区别
逻辑运算符&与&&的区别
当运算符左边的表达式为false时
a&&b 不会再计算右边的表达式,结束运算,结果为false;
a&b 会继续运算右边表达式,结果为false;
逻辑运算符&与位运算符&区别
位运算&,先将两个表达式的值按二进制位展开,然后对应的位按值进行”与”运算,结果保留在该位上。
6.跳出多重循环语句
break;break lab
continue
return
throw
面向对象(上)
1.对象与对象引用的区别
内存:对象引用存储在栈,对象在堆上;
对象引用相当于遥控器,而对象相当于电视机,对象引用能操纵对象。
2.对象作为参数传递的特点
对象作为参数传递时其实传的是参数的地址
3.对象初始化顺序
eg:FighterPlane fp=new FighterPlane()
在上述语句中,先创建一个对象“new FighterPlane()”,之后再将对象赋值给对象引用“fp”
具体过程:1.“new FighterPlane()”根据类模板产生一个对象,并在计算机的堆中为此对象分配一块内存空间,类中的属性赋值到新生成的对象中,然后对象将地址传递给引用。引用在栈中有自己的内存空间
4.类的static数据成员与非static数据成员的区别,什么用static属性
static方法是专属于类的,不属于类的任何对象
非static方法实际上是为对象服务的
static方法可以被所有对象访问,但其内部代码只能访问static属性和方法
非static方法可以访问static属性方法
static域变量储存在堆中的公共存储单元,非static域变量储存在变量的内存区
5.Java中final与finalize的区别
6.Java中float[10]arr语句是否正确?
不正确
float [ ] arr=new float[10];
“=”左面只是声明数组,执行完左面后,计算机并未分配新的内存,“=”右面才为其分配内存空间
7.Java数组元素类型为基本数据类型和引用类型时,有什么不同?
由于对象数组的数组元素类型是引用类型,这就需要我们在使用对象数组的成员前先创建新的对象,并将元素指向新的对象。
下面展示一些 内联代码片
。
String []arr=new String[10];
for(String x:arr)
System.out.println(x);
面向对象(中)
1、Java的访问控制修饰符
2、子类对于从父类继承的哪些属性与方法是可见的?
子类继承了父类的所有方法和属性,但只有public和protected方法和属性在子类是可见的(当子类和父类在同一个包中子类,或者子类和父类不在同一个包中,但父类的类前修饰符必须为public,以满足父类能被访问的前提)
3、什么是组合?组合的作用是什么?
组合和继承都是实现代码复用的方法,组合是通过对象内部属性的引用实现的,
什么是重载?重载的作用是什么?
重载定义:如果有两个方法的方法名相同,但参数不一致,哪么可以说一个方法是另一个方法的重载。
作用:
定义端(方法的提供者):使用相同的方法名(一个方法)来表示功能相同的(多个)方法。
调用端(方法的使用者): 在调用的时候,可以使用相同名字(一个名字)的方法实现不同的功能。
重载也是多态性的体现:一个内容,可以实现多个功能
什么是覆盖?覆盖的作用是什么?
定义:子类中定义一个方法,其名称、返回类型及参数签名正好与父类中某个方法的名称、返回类型及参数签名相匹配。
作用:可以在不更改父类的方法情况下创建了子类的方法,因此使程序更加安全。
面向对象(下)
1.消息
向一方发消息
public class Communicate {
public static void main(String []args){
MessageA a0=new MessageA("林海",18);
MessageB b0=new MessageB(a0);
b0.PrintMessageA();
}
}
class MessageA{
String name;
int age;
public MessageA(String name,int age){
this.name=name;this.age=age;
}
}
class MessageB{
MessageA a;
public MessageB(MessageA a0){
this.a=a0;
}
}
互发消息
public class Communicate {
public static void main(String []args){
MessageA a0=new MessageA("林海",18);
MessageB b0=new MessageB(a0);
b0.PrintMessageA();
a0.PrintMessageB();
}
}
class MessageA{
String name;
int age;
MessageB bInmsgA;
public MessageA(String name,int age){
this.name=name;this.age=age;
}
public void SetAtoB(MessageB bb){
this.bInmsgA=bb;
}
public void PrintMessageB(){
System.out.print("Bcode"+"="+bInmsgA.Bcode);
}
}
class MessageB{
MessageA a;
int Bcode=10001;
public MessageB(MessageA a0){
this.a=a0;
a.SetAtoB(this);//this指当前的对象
}
public void PrintMessageA(){
System.out.println(a.name+a.age);
}
}
2.构造方法的多态
构造方法的重载
public class poly {
public static void main(String []args){
A a0=new A(2,3,5);
System.out.println(a0.add());
A a1=new A(6,7);
System.out.println(a1.add());
A a2=new A(8);
System.out.println(a2.add());
}
}
class A{
int x=0,y=0,z=0;
public A(int x){
this.x=x;
}
public A(int x,int y){
this(x);
this.y=y;
}
public A(int x,int y,int z){
this(x,y);
this.z=z;
}
public int add(){
return x+y+z;
}
}
调用过程描述:以a0为例
1,在内存中为A类对象分配空间,这时对象的属性值都还为默认值0;
2,匹配方法的参数列表,调用第三个构造函数,第三个构造函数入栈,在栈空间中为局部变量x,y,z分配空间并且传值(2,3,5)。
3,执行到this(x,y)时,调用第二个构造函数,第二个构造函数入栈,在栈空间中为局部变量x,y分配空间并且传值(2,3)。
4,执行到this(x)时,调用第一个构造函数,第一个构造函数入栈,在栈空间中为局部变量x分配空间并且传值(2)。
5,执行到this.x=x时,并不会将局部变量x的值赋给域变量x,而是执行类中的初始化字段“int x=0,y=0,z=0;”。
6,之后执行“this.x=x”,…
构造方法的继承调用
异常
1.Java error类和exception类的区别
error不是程序需要捕获和进行处理的,当error发生时,程序将会停止。
exception发生时,虚拟机会根据异常的类别,产生对应的异常对象,程序再对异常对象进行相应的处理。
2.异常处理的两种方式
隐式声明抛出
这类异常处理要求异常必须是RuntimeException或是其子类,程序方法对异常不会做声明抛出或处理,而是交给调用该方法的地方处理,程序能编译通过,不会对可能产生异常的代码行给出提示。
例:main()方法中出现除数为零的情况,main()方法并未对异常进行处理,而是交给了调用该方法的虚拟机去处理。
public class yichangchuli{
public static void main(String []args){
int a=2;
int b=0;
int c=a/b;
System.out.print(c);
}
}
显示声明抛出
当异常是非RuntimeException或其子类时,怎样可以使程序编译通过呢?
这时可以使用显示声明抛出
方法()throws 异常类
如果方法中发生异常,且方法不做处理,交给调用该方法的地方处理。
public class yichangchuli {
public static void main(String [] args)throws IOException{
BufferedReader keyin=new BufferedReader(new InputStreamReader(System.in));
String c1;
int i=0;
String []e=new String[10];
while(i<10){
c1=keyin.readLine();
e[i]=c1;
i++;
}
}
public class yichangchuli {
public static void main(String [] args){
BufferedReader keyin=new BufferedReader(new InputStreamReader(System.in));
String c1;
int i=0;
String []e=new String[10];
while(i<10){
c1=keyin.readLine();
e[i]=c1;
i++;
}
}
非嵌套捕获处理
假如try中发生异常,紧跟其后的catch根据try中抛出的异常类型对异常进行捕获,若相同则在catch中进行处理,没有则继续比较。最后必须执行Finally语句
嵌套捕获处理
虽然try-catch-finally最终捕获异常,但try中代码块中异常语句后面的语句可能会无法执行,这时可以利用嵌套进行解决。
算术异常
public class ExampleExecption{
public stastic void main(String[]args){ int a,b,c;
a=3;b=0;c=a/b;
System.out.println(c);
}
}
空指针异常
public class yichangchuli{
private stastic int[]x;
public stastic void main(String []args)
{
System.out.println (x[0]);
}
}
数组越界异常
public class yichangchuli{
public static void main(String [] args){
String foo=args[1];
System.out.println(foo);
}
}
自定义异常类
public class yichangchuli extends Exception {
public yichangchuli(String msg) {
super(msg);
}
static void throwone() throws yichangchuli {
int a = 1;
if (a == 1) {
throw new yichangchuli("a为1");
}
}
public static void main(String[] args) {
try {
throwone();
} catch (yichangchuli e) {
e.printStackTrace();
}
}
}
3.为什么使用异常处理机制
假如一个代码块有除法的运算,为了使程序正常运行,我们可以用if-else语句判断除数是否为零,然而当除法运算很多时,这种解决办法非常麻烦,其实这些异常都可归为除零异常,如果我们用异常类解决这个问题呢?当程序运行到出现除零异常的地方时,JVM根据异常的类型生成一个指定异常类的对象,如果后面的程序没有对异常的处理,即底层不处理,则交给调用该方法的地方(JVM)进行处理,而JVM的默认处理方式就是输出异常信息,而后中断程序的运行。如果后面有对异常的处理,比如try-catch-finally语句,在try语句中产生的异常类对象,会和紧跟在try语句之后的catch语句进行匹配,匹配成功则执行catch中的语句,接着执行fianlly语句,否则继续匹配,如果没有匹配成功,会在执行完finally语句之后交给JVM进行默认处理,并且程序中断。(也可以不中断,我们可以在catch或者fianlly语句中给与程序改错的机会,这样程序就不会因为异常而终止)
实现两个对象之间互发消息
class FighterPlane
{
String name;
int missileNum;
public FighterPlane(String _name,int _missileNum){
name = _name;
missileNum = _missileNum;
}
public void fire(){
if (missileNum>0){
System.out.println("now fire a missile !");
missileNum -= 1;
}
else{
System.out.println("No missile left !");
}
}
}
class A
{
FighterPlane fp;//数据成员是FighterPlane的引用,可以变更对象,灵活性更高
public A(FighterPlane fpp){
this.fp = fpp; //A对象中拥有了FighterPlane对象的引用
}
public void invoke(){
//A中对象发送消息给FighterPlane的对象
System.out.println(fp.name);
}
}
public class Run{
public static void main(String[] args)
{
FighterPlane ftp = new FighterPlane("su35",10);
//产生A对象,并将ftp对象引用作为参数传入
A a = new A(ftp);
//发送消息,产生调用关系
a.invoke();
}
}
instanceof运用场景
java中,instanceof运算符的前一个操作符是一个引用变量,后一个操作数通常是一个类(可以是接口),用于判断前面的对象是否是后面的类,或者其子类、实现类的实例。如果是返回true,否则返回false。
组合与继承的区别以及适用场景
和继承相比较,组合有几大优点:
①不破坏封装,整体类与局部类松耦合,彼此相对独立。若是继承,子类必须继承父类方法,限制较大;父类的改动会影响子类;
②具有较好的扩展性。继承制在成员较多时可能形成多代层次,复杂且易出错,组合则没有这个缺点;
③支持动态组合,整体对象可以选择不同的局部对象。因为类与类之间相对独立,组合也会比继承更加灵活,修改更加容易;
④整体类可以对局部类包装形成新的接口;调用新方法不再受到继承中命名等诸多限制;
继承也有一些优点,比如子类可以自动获得父类的接口,调用子类时不需要纠结方法命名,可以实现多态,代码显得更简洁。但明显劣势是大于优势的。继承中,父类内部对子类是公开的,如果导致父类发生了变化,全部子类都会改变,这导致代码的不安全;
总之,在不必要应用到继承的情况下,应该优先用组合,因为更高效,更灵活。
String,StringBuffer常用API
public class text1
{
public static void main(String[] args)
{
String s="accdedcba" ;
System.out.println("字符串的长度为:"+s.length());
System.out.println("字符c第一次出现的位置:"+s.indexOf('c'));
System.out.println("字符c最后一次出现的位置:"+s.lastIndexOf('c'));
}
}
StringBuffer sb=new StringBuffer();
StringBuffer s1=sb.append(4).append(false).append("haha");
sb.insert(1,"qwe");
System.out.println(sb==s1);
System.out.println(sb);
StringBuffer sb=new StringBuffer("abce");
sb.replace(2,3,"nab");//包含头不包含尾
sb.setCharAt(2,'q');
System.out.println(sb.toString());
StringBuffer sb=new StringBuffer("abce");
sb.delete(0,sb.length());//清空缓冲区
sb.deleteCharAt(2);
System.out.println(sb.toString());
String,StringBuffer,Stringbuilder的共同点,区别
String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且浪费大量优先的内存空间。
StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量 可变类,速度更快,多线程操作字符串,线程安全。
StringBuilder可变,线程不安全,是单线程操作字符串。
为什么不建议在for循环中使用"+"进行字符串拼接
因为每进行一次拼接,都需要创建一个新的String类对象,浪费了大量的空间,比较好的做法是,在for循环外部先将String转换成StringBuffer,循环内部调用append()方法进行拼接,循环完成后,再将StringBuffer转为String
编码与解码
编码:将Unicode转为本地字符集
解码:将本地字符集转为unicode字符
线程
volatile有什么作用?
假如有一个对象A里面有一个boolean变量a,值为true,现在有两个线程T1,T2访问变量a,T1把a改成了false后T2读取a,T2这时读到的值可能不是false,即T1修改a的这一操作,对T2是不可见的。发生的原因可能是,针对T2线程,为了提升性能,虚拟机把a变量置入了寄存器(即C语言中的寄存器变量),这样就会导致,无论T2读取多少次a,a的值始终为true,因为T2读取了寄存器而非内存中的值。声明了volatile或synchronized 后,就可以保证可见性,确保T2始终从内存中读取变量,T1始终在内存中修改变量。
/*
这个模拟程序一共分为三个时间,烧开水,洗茶杯,泡茶
烧开水的时候可以洗茶杯
必须烧完开水,洗完茶杯以后才能开始泡茶
*/
import java.util.Date;
public class Tea implements Runnable{
static Date date = new Date();
public static void main(String[] args) throws Exception{
HeatUpWater h1 = new HeatUpWater();
WashCup w1 = new WashCup();
Tea m1 = new Tea();
Thread t1 = new Thread(h1);
Thread t2 = new Thread(w1);
Thread t3 = new Thread(m1);
try{
t1.start();
t2.start();
t2.join();
t1.join();
}catch (Exception e){
System.out.println("Error!");
}
t3.start();
}
public void run(){
System.out.println(date+" 正在泡茶");
try{
Thread.sleep(1000);
}catch (Exception e){
}
}
}
class HeatUpWater implements Runnable{
static Date date = new Date();
public void run(){
int time = 10;
while(time != 0){
System.out.println(date+" 正在烧水");
time--;
try{
Thread.sleep(1000);
}catch (Exception e){
System.out.println("Heat up water is in the Error!");
}
}
}
}
class WashCup implements Runnable{
static Date date = new Date();
public void run(){
int time = 5;
while(time != 0){
System.out.println(date+" 正在洗茶杯");
time--;
try{
Thread.sleep(1000);
}catch (Exception e){
}
}
}
}
javaIO
java中流的分类
java中流可分为字节流和字符流,字节流以字节为基本单位,而字符流以字符为基本处理单位。字节流和字符流又可派生出一系列类。
InputStream和OutputStream的子类
字节流 InputStream/OutputStream(抽象类)
->子类 (文件流)FileInputStream /FileOutputStream(底层流,用来包装高级流)
->子类 (字节缓冲流)BufferedInputStream/BufferedOutputStream(文件复制)
InputStream fis=new FileInputStream(" “);
字节缓冲流 BufferedInputStream/BufferedOutputStream
在BufferedInputStream中包装一个InputStream
BufferedInputStream bis=new BufferedInputStream(new InputStream(” "));
字符流 Reader/Writer(抽象类)
->子类 InputStreamReader/OutputStreamWriter(底层流,用来包装高级流)
->子类 (字符缓冲流)BufferedReader/PrintWriter (内容读写)
字符流中包装了字节流
InputStream is=new FileInputStream("");
Reader isw= new InputStreamReader(is);
字符缓冲流 BufferedReader/PrintWriter
在BufferedReader中包装一个InputStreamReader
BufferedReader in= new BufferedReader(new InputStreamReader(" "));
ObjectInputStream / ObjectOutputStream ->操作对象,字节位单位
字节流和字符流的转换
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* InputSteramReader把字节流转换为字符流
* 字节流转换为字符流
*/
public class ConvertInDemo {
public static void main(String[] args) {
InputStream is = System.in;
//要想使用字符流的高效缓冲区来操作字节流需要转换
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//定义要写入的文件流
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter("c.txt"));
} catch (IOException e1) {
e1.printStackTrace();
}
String line = null;
try {
while((line = br.readLine())!=null){
if("exit".equals(line)){
break;
}
bw.write(line);
//换行
bw.newLine();
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}finally{
if(bw !=null){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
/**
* OutputSteramWriter把字符流转换为字节流
* 字符流转换为字节流
*/
public class ConvertOutDemo {
public static void main(String[] args) throws IOException {
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new FileReader("c.txt"));
//创建字符流向字节流转换的对象
bw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while((line = br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}finally{
if(bw != null){
bw.close();
}
if(br != null){
br.close();
}
}
}
}
过滤流的作用
过滤流(filter stream)也称为包装流,是为某种目的过滤字节或字符的数据流。
基本输入流提供地读取方法只能用来读取字节或字符,而过滤器流能够读取整数值、双精度值或字符串,但需要一个过滤器类来包装输入流。
FilterInputStream 类和 FilterOutputStream 类分别是 DataInputStream 类和 DataOutputStream 类的父类,它们分别实现了 DataInput 和 DataOutput 接口,该接口定义了独立于具体机器的带格式的读写操作,从而可以实现对 Java 中的不同基本类型数据的读写。
对象的序列化和反序列化
(1)Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程;
(2)**序列化:**对象序列化的最主要的用处就是在传递和保存对象的时候,保证对象的完整性和可传递性。序列化是把对象转换成有序字节流,以便在网络上传输或者保存在本地文件中。序列化后的字节流保存了Java对象的状态以及相关的描述信息。序列化机制的核心作用就是对象状态的保存与重建。
(3)**反序列化:**客户端从文件中或网络上获得序列化后的对象字节流后,根据字节流中所保存的对象状态及描述信息,通过反序列化重建对象。
实现Java对象序列化与反序列化的方法
对象序列化有如下三种方法:
1)若User类仅仅实现了Serializable接口,则可以按照以下方式进行序列化和反序列化
ObjectOutputStream采用默认的序列化方式,对User对象的非transient的实例变量进行序列化。ObjcetInputStream采用默认的反序列化方式,对对User对象的非transient的实例变量进行反序列化。
2)若User类仅仅实现了Serializable接口,并且还定义了readObject(ObjectInputStream in)和writeObject(ObjectOutputSteam out),则采用以下方式进行序列化与反序列化。
ObjectOutputStream调用User对象的writeObject(ObjectOutputStream out)的方法进行序列化。ObjectInputStream会调用User对象的readObject(ObjectInputStream in)的方法进行反序列化。
3)若User类实现了Externalnalizable接口,且User类必须实现readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法,则按照以下方式进行序列化与反序列化。
ObjectOutputStream调用User对象的writeExternal(ObjectOutput out))的方法进行序列化。
ObjectInputStream会调用User对象的readExternal(ObjectInput in)的方法进行反序列化。