面试题错题总结(不断更新完善中)
- java 关键字
- java 标识符
- 赋值语句
- 包装类的注意点
- 8种基本类型
- 拆箱和装箱
- Java数据库jdbc连接模式
- 继承
- Java引用传值和值传递
- 数据类型的比较
- 内部类创建实例
- 泛型
- 线程
- 异常
- static使用
- Java7 新特性
- Java数据类型的转换
- instanceof
- 接口
- 静态代码块、构造函数、构造代码块
- 类的修饰符
- final
- 局部变量和成员变量重名
- Java 内部类外部类文件名
- 数组
- Java 命令行操作
- 值计算
- 本地方法
- 抽象类
- StringBuilder、StringBuffer
- String
- window下的盘符
- 反射
- 类型的向上、向下转型
- 多态
- 注解
- &&、&、||、|
- 如何根据等式去计算相应的进制数
- java的跨平台性
- 构造函数
- Object
- 重写、重载
- java修饰符
- 外部类
- 内部类
- .java源文件
- 方法
- for语句执行流程
- 序列化
- 线程安全
- java注释
- java 参数的局部优先
- java import与package
- Object 方法
- 引用类型的赋值
- equals 比较对象的类型转化
- 静态和实例的调用关系
- java 跨平台
- java 虚拟机
- 方法签名
- java集合
- 运算优先级
- java
- 局部变量
- java中赋值坑
- 形参
- null的作用
- Math类中方法
- 自增
- Switch选择判断
java 关键字
java 标识符
java之中的标识符的定义格式:由字母、数字、_、$所组成,其中不能以数字开头,不能是Java中的保留字。
赋值语句
int a = 0;
a++//a = a+1;
包装类的注意点
在java.lang.包下,对基础类类型的包装类,是不可以继承,他们类前面的修饰符是final。还有Math类也是一样,他的里面是一些对数学的操作。
8种基本类型
整数类型默认是int类型
浮点类型默认是double
基本类型定义
double a = 4;
float b = 3.4f;//如果你不加f,会默认是double,需要强转
float c = 3.4F;
long d = 3;
long e = 4l;
long f = 4L;
Double d = 3.0//会把double类型的3.0自动装箱为Double
在上面的大小写申明数据类型,大小写无所谓。
基本类型的转换
由这个图,可以看出你可以直接把double类型的变量赋值给int,编译不会通过,需要强转。
这里需要注意的就是long类型和float,double ,都会自动转成float、double(你就这么记,低于int,都会转成int,高于int的,整数类型转成浮点类型,double老大)
另外还有一个注意:字符的范围没有负。
short a = 1;
a+=1;//+=这种运算short会自动转型
a = a+1;//这种编译不可以通过,需要你手动强转
拆箱和装箱
发生的时机是在:引用类型与值类型之间。
基本数据类型转化成包装类是装箱 (如: int –> Integer)。
包装类转化成基本数据类型就是拆箱 (如:Integer –> int)。
Java数据库jdbc连接模式
JDBC连接 数据库 的时候,在各个数据库之间进行切换,基本不需要动太多的代码,甚至丝毫不动,原因就是JDBC提供了统一接口,每个数据库提供各自的实现,用一个叫做数据库驱动的程序来桥接就行了
继承
在子类和父类之间,如果子类的方法是private的,那么子类无权访问,如果是公开,那么子类是可以访问;子类可以直接在方法中直接调用,而不需要采用构造对象(这种自己亲自测试),这个和是不是静态、实例方法无关。在实例方法中可以访问类或者实例访问,而在类方法中只能访问类方法。
java 继承的传递性
a extends b
c extends a
-->c extends b
继承中的修饰符
如果在继承一个类的时候,子类的修饰符绝对不能比父类的小。
继承中private 修饰
子类继承于父类,需要注意的是对于父类的private修饰的字段和方法,子类是继承的,但是还无法直接访问。
继承中构造函数的注意点
继承中的构造函数,子类首先找无参构造,找不到就必须调用父类的有参。
- 父类没有写构造函数(系统默认有一个无参构造函数),子类可以不写构造函数(可以理解为:子类的系统默认构造函数,默认调用了super();)
- 如果父类有无参构造函数,子类可以不写构造函数(同上理解)
- 如果父类有有参构造函数,则子类必须在自己的构造函数中显示的调用父类的构造函数即super(参数名),因为你如果定义一个有参的构造函数,默认的就不会在提供了,如果你在定义无参的构造函数,在子类构造函数调用前,调用一下子类,可不必显示调用有参的父类构造函数。如果没有定义无参,就必须显示调用一下父类的有参构造函数(反正就一句话,你必须调用一下父类的构造函数)
假设父类有name1这个属性,并且有参构造(这里没有显示定义无参)
你在子类构造必须这样做,编译才不会有问题。
private String name2;
public Test293(String name1,String name2) {//把子类的属性加到父类的参数的构造函数的后面,或者单独写,也可以。
super(name1);
// TODO Auto-generated constructor stub
}
这里,还有一个问题,如果父类有多个有参的构造函数,子类写哪个,答案随便哪个都可以,你写了就好。
假如你在父类定义有参构造函数,并且又提供无参构造函数,子类就不需要调用父类的有参构造,它会自己找无参调用。
总之一句话,只要有继承,那么在执行子类的构造函数的时候,它会主动去找父类的无参的,找到ok,找不到,(也就是你父类有参的构造函数,系统就不默认提供无参),这时,在子类,编译强迫你要实现子类有参构造函数的调用,否则编译不通过。
public Test293(String name2) {
// TODO Auto-generated constructor stub
}
继承中的this、super
super()、this() 是针对构造函数的,可以带相应的参数。
super、this 是针对对象,可以直接调用方法或者属性。
我们在继承关系,常常看到如下:
public void show() {
// TODO Auto-generated method stub
super.show();//你可以注释掉,不去使用父类的方法。自己去覆盖它。
}
super 是指向父类的对象,你可以通过这个,去显示调用父类的构造函数、方法、字段等等(前提必须是公开的)。它有一个注意就是在子类调用父类构造函数,必须是在第一行。这里我自己测试过了,只有在构造函数中才有这个限制,在方法中或者通过super调用属性,则没有限制。(必须保证先执行父类的构造函数)
在继承的子类中,只要执行子类的构造函数,就一定执行父类的构造函数,不管你有没有显示调用super()。而在父类中,就不一样,如果你没有显示调用父类的方法,父类的方法是不会在执行子类方法执行的。(其实这点,你想想也能懂,如果能执行,不是乱了)
this 是指向本类的对象,你可以通过这个,去显示调用自己类的构造函数、方法、字段等。它有一个注意就是在构造函数中使用,调用其他构造函数,它必须是在第一行,而在构造函数通过this去调用属性,方法中调用本类的其他方法,则没有该限制。
另外在构造方法中是不可以同时使用super、this。(它们两必须是在第一行,不要想同行,行不行,肯定不行)而在方法,则用可以这样同时玩,挺66666。
我们知道可以通过super关键字去调用父类方法,但这不是一定,我也可以通过构造父亲的对象,直接调用父类的方法。
另外还有一个注意的地方,如果父类和子类的方法名字一致,用super关键字去调用父类的方法,如果父类和子类的名字不一致,子类可以用this关键字去调用父类的方法,为啥呢,继承了不就是子类自己的,this是当前对象,当然可以去调用,但是这种必须名字不一样,否则是不可以的。
子类有父类同名的方法
这是就会有两种情况,一种是重写,一种是重载,这个你去判断。重载也是可以出现在子类和父类中。
在继承中方法重写的规则
- “三同”:即方法名相同,形参列表相同,返回值类型或者父类的子类。
- “一小”:子类方法声明抛出的异常比父类方法声明抛出的异常更小或者相等;
- “一大”:子类方法的访问修饰符应比父类方法更大或相等。
继承的synchronized
用synchronized关键字来修饰方法,如果子类继承,实际上继承的是方法,而并非synchronized 也继承了,所以子类不具有同步性,如果同步需要你手动synchronized关键字修饰方法,不过现在在继承的时候,java 默认帮你加上,你可以选择不加。
Java引用传值和值传递
- 基本数据类型传值,对形参的修改不会影响实参,形参和实参指向的不是同一个内存地址,同一个对象,指向的是拷贝的。
- 引用类型传引用,形参和实参指向同一个内存地址(同一个对象),所以对参数的修改会影响到实际的对象。这个主要有数组,对象,其中数组可以是各种基本类型的,都符合该条件。
- String, Integer, Double等immutable的类型特殊处理,可以理解为传值,最后的操作不会修改实参对象。(因为这写类型是不可变,还是原来的值,相当与传值)
https://www.cnblogs.com/binyue/p/3862276.html
小例子
public static void main(String sgf[]) {
StringBuffer a=new StringBuffer("A");
StringBuffer b=new StringBuffer("B");
operate(a,b);
System.out.println(a+"."+b);
}
static void operate(StringBuffer x,StringBuffer y) {
x.append(y);
y=x;
}
}
运行结果:AB.B
首先要明白StringBuilder是引用类型,引用传值。画图解释:
需要注意append的这个方法,是在原先的s内存地址,添加内容,并不会重新生成新的内存地址。
public static void main(String[] args) {
String s ="javahellook";
System.out.println(s);
System.out.println(s.hashCode());
show(s);
System.out.println(s);
}
public static void show(String s) {
System.out.println(s.hashCode());
s = s.substring(4, 9);
System.out.println(s.hashCode());
}
数据类型的比较
首先,我们先明白在Java中数据分为基础类型和引用类型,这个就不细说了;其次,我们要明白=和equals分别在什么时候使用,=在基础类型的比较就是值比较,而在引用类型中,就是比较
所指对象的地址。注意基础类型是没有equals,只有引用类型才有,因为他们把数据类型封装成对象。而对于引用类型的equals要看它实现的是哪种比较,则需要看它的是如何实现的。(查看源码),比如常见的Integer、Long、Double是对值的比较。有了这些,看下面就容易了。(这段参看了https://www.cnblogs.com/dolphin0520/p/3592500.html)
Integer i = 42;
Long l = 42l;
Double d = 42.0;
//System.out.println(i==l);//不相容的操作符,不好比较
//System.out.println(i==d);//不相容的操作符,不好比较
//System.out.println(l==d);//不相容的操作符,不好比较
//System.out.println(i.equals(d));//看源码就知道,double并不是Integer的实例
/*
* public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
*/
//System.out.println(d.equals(l));//本例和上面的一样看Double源码就知道原因
//System.out.println(i.equals(l));//本例和上面的一样看Double源码就知道原因
Double p = 0.2;
Double k = 0.1;
System.out.println(p==k);
//double类型则不能使用双等号来比较大小,如果使用的话得到的结果将永远是不相等
//但是如果使用double,就没有事,反正都是比较的是值。
//该方法可以将double转换成long型数据,从而可以使double按照long的方法(<, >, ==)判断是否大小和是否相等。
//参考了http://blog.csdn.net/liuweiyuxiang/article/details/71104712
//System.out.println(Double.doubleToLongBits(0.01) == Double.doubleToLongBits(0.01) );
//System.out.println(Double.doubleToLongBits(0.02) > Double.doubleToLongBits(0.01) );
//System.out.println(Double.doubleToLongBits(0.02) < Double.doubleToLongBits(0.01) );
/* Double源码
public boolean equals(Object obj) {
return (obj instanceof Double)
&& (doubleToLongBits(((Double)obj).value) ==
doubleToLongBits(value));
}*/
内部类创建实例
- 静态内部类
- 非静态内部类
public static class inner{
public void show() {
System.out.println(“ttt”);
}
}
public static void main(String[] args) {
/*Test102 t = new Test102();//创建非静态内部类的方法,需要先进行外部类对象
* 的创建。
inner i = t.new inner();
i.show();*/
Test102 t = new Test102();
Test102.inner i = new Test102.inner();
//对于静态内部类,该内部类属于外部类的一部分,所以直接调用方法那样,调用内部类的构造方法。
i.show();
}
泛型
我们可以看到,泛型在Java中大多是用在集合中。泛型只是在编译期保证对象类型相同的技术,即我们在编译期添加不同类的对象到集合,编译器是会报错的,需要真正的在代码运行期,jvm是忽略泛型的存在。
static List a = null;
static List b = null;
static List c = null;
static List
线程
线程创建的两种方式:
- 拓展Thread(不赞成使用)
- 实现runable接口
还有一种是基于callable、future、
Mythread1 m = new Test104().new Mythread1();
m.start();//这种是基于拓展线程类的线程开启的方法,直接创建线程类对象,调用start方法
Thread t = new Thread(new Test104().new Mythread2());
t.start();//这种是基于实现runable类的线程开启的方法。先创建Thread对象,传一个runable进去。
}
class Mythread1 extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
System.out.println("我是线程1");
}
}
class Mythread2 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("我是线程2");
}
}
上面的两种方式,不管是哪种,都是需要对run方法进行重写,我们通常将run方法中的称为线程体,我们是可以通过start和调用run方法来执行,但是内部的思想是巨大。
通过start方法启动线程,执行线程体内的内容,是真正的创建了一个线程,真正实现了多线程。无需等待run()方法中的代码执行完毕,就可以接着执行下面的代码。此时start()的这个线程处于就绪状态,当得到CPU的时间片后就会执行其中的run()方法。这个run()方法包含了要执行的这个线程的内容,run()方法运行结束,此线程也就终止
通过run方法启动线程其实就是调用一个类中的方法,当作普通的方法的方式调用。并没有创建一个线程,程序中依旧只有一个main主线程,必须等到run()方法里面的代码执行完毕,才会继续执行下面的代码,这样就没有达到写线程的目的。
综上所述,基于上面两点的分析,我们可以看出,开启线程的方法只有一种就是start,而run方法是骗我们的,虽然也执行了线程体,但是它没有创建一个新线程还是main主线程在操作,亲,你看懂了没!!!!!(参看http://blog.csdn.net/lai_li/article/details/53070141?locationNum=13&fps=1)
public static void main(String[] args) {
Mythread1 m = new Test104().new Mythread1();
m.run();
m.start();//
System.out.println("我好了");
}
class Mythread1 extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
for(int i =0;i<100;i++) {
System.out.println("1111111111");
}
}
}
//可以看出如果是基于调用run方法的,那么该print一定是掉在for循坏介绍后,而开启了start,开启了一个线程,如果该线程分配到cpu,就执行,并不影响print打印执行。
执行线程的run和start
执行线程的run方法和start方法,都可以执行线程的线程体,区别如下:
调用线程start方法:线程和main线程是异步执行。
调用线程的run:实际上就是好像调用某个类的方法,这时线程并没有真正的启动,该方还是在main线程中执行,是同步关系。
异常
异常结构图
异常的使用形式
就下面三种,不存在其他的
try {
} catch (Exception e) {
// TODO: handle exception
}
//try....catch
try {
} finally {
// TODO: handle finally clause
}
//try...finally
try {
} catch (Exception e) {
// TODO: handle exception
}finally {
}
//try...catch...finally
异常的分类
- error。当资源不足、约束失败、或是其它程序无法继续运行的条件发生时,就产生错误。程序本身无法修复这些错误的。例如,VirtualMachineError就属于错误。编译器并不会对对它进行错误检查
- 被检查异常,此类异常,要么通过throws进行声明抛出,要么通过try-catch进行捕获处理,否则不能通过编译。
- 运行时异常,虽然Java编译器不会检查运行时异常,如果空指针异常、数组越界,但是我们也可以通过throws进行声明抛出,也可以通过try-catch对它进行捕获处理。这种异常,程序可以在代码中避免。
参考:https://www.cnblogs.com/skywang12345/p/3544168.html
异常处理流程
出现运行时异常后,系统会把异常一直往上层抛,一直遇到处理代码。如果没有处理块,到最上层jvm,它不能不干,最后它得处理。
捕获到的异常不仅可以在当前方法中处理,还可以将异常抛给调用它的上一级方法来处理。
发生异常之后,程序是否还能正常执行
for(int i =0;i<10;i++) {
System.out.println("我在操作"+i);
if(i==3) {
int c = 3/a;
}
}
该方法在a=0的时候,会引发运行时异常,你对它没有进行处理,当执行到这步的时候,jvm找相应的异常处理,找不到,它就要靠jvm自己处理,所以程序终止了,但是如果在异常栈中,找到了相应的处理,那么一场处理结束,程序还是会继续执行的。
public static void show(int a) {
for(int i =0;i<10;i++) {
System.out.println("我在操作"+i);
if(i==3) {
try {
int c = 3/a;
} catch (ArithmeticException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
异常中返回值问题
public static void main(String[] args) {
System.out.println(show());
}
public static String show() {
try {
URL url = new URL("/www.baidu.com");
System.out.println("try");
return "我是try返回";
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("catch");
return "我是catch返回";
}finally {
return "我是finally";
}
}
上述测试代码,有以下几点注意:
- 当有正常的程序,进入到有异常的语句块中,执行到引起异常的语句,如果没有异常,那么该该程序自动走下去,不走catch语句块;当有异常时,执行异常语句之后,程序不会往下面走,而是立即执行语句块。
- finally语句块的东西,不管程序以否有异常,都是需要执行的。
- 如果finally中有return 会覆盖try、catch语句块中的return,并且提前结束,所以不建议把返回值写在finally中。
- 我们在try中写return的时候一定要注意,可能会编译出错,原因简单,编译器认为try的语句都可能出现异常,你在程序如果出现异常,那么起不是没有返回值,所以它不让你通过。
public static int aMethod(int i) throws Exception
{
try{
return 10/i;
}
catch (Exception ex)
{
throw new Exception("exception in a aMethod");
}finally{
System.out.printf("finally");
}
}
但是这样却可以通过,原因很简单,你用了throw关键字,编译器就知道该程序肯能出现异常,如果你程序正常运行,正常返回结果,如果异常,会有相应的处理。有人又说,按你这样,我直接在catch里面处理一下,问题是你知道是处理,编译器怎么知道,关键是要用throw申明一下。
- catch 语句块中是可以有返回值的,因为程序出现异常,肯定是会走catch块的。
异常返回变量的值变化
public static int show() {
int a;
try {
a = 1;
return a;
} finally {
// TODO: handle finally clause
a = 2;
//return a; 不加这句,返回值为1,加了这句,返回值是2,
//当程序执行到try{}语句中的return方法时,它会干这么一
//件事,将要返回的结果存储到一个临时栈中,然后程序不会
//立即返回,而是去执行finally{}中的程序, 在执行a =
//2时,程序仅仅是覆盖了a的值,但不会去更新临时栈中的
//那个要返回的值 。
}
}
你可以这么理解,如果try或者catch中有返回值,而finally没有返回值,那么在finally中修改的返回值,就不会影响try、catch返回值。如果finally中有,那么就覆盖try、catch中的。
throw与throws
java 中try是系统能检测到,并且自动抛出的异常。
自定义异常是Java系统无法监测到的异常,必须由用户抛出(throw)。
手动抛出了之后,有两种处理办法:
- throws是用来申明方法,一般配合throw来使用,你既然抛出了(出现了异常),我就告诉其他人,调用该方法的人,不强求你一定要处理,你不处理,到最好虚拟机处理,你处理了更好。
-
try…catch 去处理。
- throw关键字用于方法里面,throws用于方法的声明上。
- throw关键字用于方法内部抛出异常,throws用于方法声明上抛出异常。
- throw关键字后面只能有一个异常(throw后面是扔出一个一个异常对象哦),throws 可以声明多个异常。
- 如果一个方法中抛出了异常,那么throw 后面的任何代码(包括return)就不会再执行了。
public static void main(String[] args) {
try {
show();
} catch (RuntimeException e) {
// TODO: handle exception
}
}
public static void show() throws RuntimeException{
try {
int i = 100/0;
System.out.println(i);
} catch (Exception e) {
// TODO: handle exception
System.out.println(1);
throw new RuntimeException();
}finally {
System.out.println(2);
}
System.out.println(3);//如果没有手动抛出,则这里会执行
}
try 是直接处理,处理完成之后程序继续往下执行,throw则是将异常抛给它的上级调用者处理,你知道什么时候异常被处理好了,这个是不确定的,程序便不往下执行了。
由这个可以将例子变一下:
public static void main(String[] args) {
show();
}
public static void show() throws RuntimeException{
try {
throw new RuntimeException();
} catch (Exception e) {
// TODO: handle exception
System.out.println(1);
}finally {
System.out.println(2);
}
System.out.println(3);
}
这时3又可以打印,这时为什么呢,因为这时的throw在try中,会直接到catch中处理,处理完成之后,程序后面当然自然执行。(记住,异常只有处理完,程序继续才能执行)
- throw 后面不可以在跟其他代码,按照上面的说明,后面读不执行,你还在它后面写代码,有啥用,编译不会通过。
异常链
异常链:是指发生一个异常,在异常处理的时候,我们还没有能力处理,只能抛给上级,我们通过throw关键字,又手动的抛出一个异常,形成一个异常链。
public static void show(int a) throws Exception {
for(int i =0;i<10;i++) {
System.out.println("我在操作"+i);
if(i==3) {
try {
int c = 3/a;
} catch (ArithmeticException e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new Exception("我又有异常了");
}
}
}
}
多个异常捕捉的顺序
是从上到下,规定异常的子类在前,父类在后,因为子类异常的比较细,如果父类在上,那么捕捉到父类,下面的catch块,都不会执行。并且需要注意,从上到下捕获,如果,捕获了,那么下面是不会执行的,所以,多重catch,只有catch一个异常,不会是多个。
jdk 1.7提供的新的捕获多个异常方法
try {
int a = 1/0;
} catch (ArrayIndexOutOfBoundsException |ArithmeticException e1) {
// TODO: handle exception
}
这种方式虽然简单,但是捕获的异常全部都是平级关系,而且这个里面的异常,不可以放父类,子类异常,子类全被父类包了,多个异常是共用的一个对象。
static使用
static 的成员属性与成员属性初始化
static修饰
public static int a = 1;
public int b = 2;
public static void main(String[] args) {
Test291 t = new Test291();
t.b++;
Test291.a++;
Test291 t1 = new Test291();
t1.b++;
Test291.a++;
Test291 t3 = new Test291();
t3.b++;
Test291.a++;
System.out.println("t "+t.b+" "+Test291.a);//3 4
System.out.println("t1 "+t1.b+" "+Test291.a);//3 4
System.out.println("t2 "+t1.b+" "+Test291.a);//3 4
由这个例子,我们可以看出实例属性,随着new 对象 每次都初始化,而类变量,在第一次类加载的时候,就初始化,之后的属性的修改都在基础上。一个成员变量被static修饰,那么这个类的所有实例全部共享这个变量。
public static void main(String[] args) {
show();
}
public static void show() {
System.out.println("show");
}//可以在类方法直接调用另外的类方法,无需前面加类的名字。也可以加,但是不强求。
static中this和super
注意在static中super和this这两个是绝对不能使用,原因static是对于类而言,而this是指向本身的对象,super是指向父类的对象,两个完全不在一个层面,谈不起来。
static修饰变量
我们都知道static修饰的变量是属于整个类,称之为全局变量,而局部变量是属于方法,所以static是不能修饰局部变量。
public static void main(String[] args) {
static int z = 2;//编译不会通过
}
我们都知道静态变量在静态方法之前先加载的,所以如果在方法内设置静态变量,可想而知,方法都没加载,你能加载成功方法内的静态变量? 这里如果是实例方法,那就更不用说了,肯定不行。
static变量赋值
静态变量初始化值和在静态代码块初始化值,它们实际上在同一个级别,就看谁在上,谁在下,就谁先执行。他们都是随着类的加载一起来的。
static 修饰的成员属性和方法
既可以通过类名调用,也可以通过实例对象调用
static 修饰的方法是否可以继承
static 修饰的常量或者是方法是可以被继承的,但是却隐藏了重写。所以static 你就是重写也没用,玩不成重写,那么也实现不了多态了。
public class Test277 {
public static void show() {
System.out.println("我是父");
}
}
public class Test278 extends Test277 {
public static void show() {//你在这可以自己写一个和父类方法一致的方法
System.out.println("我是子");
}
public static void main(String[] args) {
Test277 t = new Test278();
t.show();//这里如果按照的是动态分派,show方法应该是test278的,而你在编写的时候,会发现只有Test277的,隐藏了Test278。
}
}
重写是基于动态绑定,而static 是随着类的加载,就绑定,它属于静态绑定。
Java7 新特性
switch原来只支持对byte、char、int,在Java7之后对字符串也支持。
String s ="xjl1";
switch (s) {
case "xjl":
System.out.println("你猜对了");
break;
default:
System.out.println("你猜错了");
break;
}
Java数据类型的转换
- 自动类型转换
顺序:byte,short,char,int->long->float->double
byte 1个字节 表示数范围 -128~127
short 2个字节 表示数范围 -2(15)~2(15)-1
char 2个字节
int 4个字节 -2(31)~2(31)-1
long 8个字节
float 4个字节
double 8个字节
从左往右优先级越来越高
- 强制类型转换
在上述关系中,如果是从右往左,那么需要加上(类型),表示强制转换,会丢失精度。
int a;
byte b;
long c;
short s;
float h;
double d;
char t ;
b = t;
t = b;
s = t;//char、byte、short三种之间均不可直接赋值
a = b;
a = s;
a = t;//char、byte、short可以直接赋值给int
c = a;//int可以直接赋给long
a = c;//long不可以赋值给int,需要强制转换。
h = a;//int可以直接赋值给float
a = h;//float不可以直接赋给int,需要强制转换。
d = h;//float不可以直接赋给double
h = d;//double不可以直接赋给float,需要强制转换。
instanceof
该方法是返回一个布尔值来指出,这个对象是否是特定类的实例或者是该特定类子类的实例,在或者是一个实现指定接口的类的实例。
注意:
Test128 t = null;
System.out.println(t instanceof Test128);//该例t还是不是Test128的实例,它只是一个空对象。所以是false
在该方法中,如果对象经过继承,强制转换,或者是自动转换,那么,该对象就是继承的父类或者该子类、转换对象的实例。
接口
接口实际上类似于一个协议,规定,里面都是制定的规格,实现接口,就必须按照这些来。
接口中字段和方法的申明
public static final String NAME="XJL";
public abstract void show();
//注意他们的修饰符
接口的修饰符,只能用public(接口是公开让大家使用(修饰符绝对不可可以是private),所以必须public) 、abstract修饰,不可以是其他,字段可以用static和final修饰。
为什么是public:因为接口必然是要被实现的,如果不是public,这个属性就没有意义了;
为什么是static:因为如果不是static,那么由于每个类可以继承多个接口,那就会出现重名的情况;
为什么是final:这是为了体现java的开闭原则,因为接口是一种模板,既然是模板,那就对修改关闭,对扩展开放。
对修改关闭,对扩展开放,接口是对开闭原则的一种体现。所以接口的属性用 public static final 修饰。
接口中不可以有具体的方法实现,只能是抽象方法,而抽象类中可以有抽象方法或者是具体方法。
一个类要实现接口,就必须实现接口的所有的抽象方法。
两个接口之间不可以相互实现,但是可以继承,因为你自己本来就是一个接口,所以不请求你重写父类的抽象方法,如果你想重写,那么提供一个default修饰的方法。
default这个是啥,原来java接口,是不允许定义方法体的,那怎么好重写呢。
接口1.8 新特性
原因:在java1.8之后,接口提供default方法和static方法。
如果你想在接口定义一个方法并且还有具体的实现,那么请在方法前加default修饰,否则编译不会通过。default修饰的方法,接口的实现类可以直接调用。
接口中的static方法,类似类的静态方法,可以通过接口名直接调用。
interface Study{
abstract void study();
public static int s() {
return 0;
}
public default void s1() {
}
}
接口的多继承
Java中接口是可以继承多个接口的,这个和类不同。
不允许类多重继承的主要原因是,如果A同时继承B和C,而B和C同时有一个D方法,A如何决定该继承那一个呢?
但接口不存在这样的问题,接口全都是抽象方法继承谁都无所谓,所以接口可以继承多个接口。
参考:https://www.cnblogs.com/deepbreath/p/5015642.html
public interface A extends A2,A3{
public void fun();
}
首先你必须要知道接口中是可以有字段,实现接口,可以重写它的方法,但是不可以修改它的字段值。这点我测试了,去除字段修饰符的final,也不可以,(建议还是加上final)
我们可以通过接口,来间接实现多继承
接口可以继承于接口
注意:接口是可以申明定义对象,但是无法实例对象,实例必须还是要通过其具体实现类。
接口构造函数
接口不可以有构造函数:
构造方法用于初始化成员变量,但是接口成员变量是常量,无需修改。接口是一种规范,被调用时,主要关注的是里边的方法,而方法是不需要初始化的。
类可以实现多个接口,若多个接口都有自己的构造器,则不好决定构造器链的调用次序
静态代码块、构造函数、构造代码块
{
System.out.println("我是子类构造代码块");
}
static {
System.out.println("我是子类静态代码块");
}
public Test136() {
super();
// TODO Auto-generated constructor stub
System.out.println("我是子类构造函数");
}
public static void main(String[] args) {
Test135 t = new Test136();
}
由上述的测试代码,可以得出以下结论:
- 静态代码块》构造代码块》构造函数 执行顺序。
- 构造代码块每创建一个对象,都会执行。构造代码块执行是在构造函数之前
- 当继承于其他会该顺序又变化:父类静态代码块>子类的静态代码块>父类的构造代码块>父类的构造方法>子类的构造代码块>子类的构造方法
4.注意继承中子类构造函数执行,都会执行父类(这里假设是无参的),但是构造代码块不一样,各执行各的。
注意:静态成员初始化和静态代码
静态代码块
- 静态代码块,是在程序第一个实例创建的时候调用,在类第一次加载的时候执行,之后在创建类实例的时候,都不会执行。
- 在静态代码块,进行只需要执行一次的操作。
- 静态代码块,是由程序调动,不可以由人工调用。
- 多个静态代码块,调用时从上到下。
类的修饰符
外部类:
only public, abstract & final are permitted
内部类:
绝大多数的修饰符都可以
final
我们知道通过final可以来修饰一个常量。以下两种其实都可以,主要使用static在前的这种。
static final int a = 1232;
final static int a1 = 234;
final 修饰变量默认初始化
final 修饰类的成员变量,如果没有初始化,编译是不会通过。
但是在方法中,final 修饰局部变量,可以先申明,在赋值。
在java里面修饰词 final修饰过的变量的值是不可以被修改的。在外部声明的变量在这个类一调被用就初始化了,如果你没有对final修饰过的变量赋值,编译器初始化这个类的时候发现一个不可被修改的变量没有值,编译器必然报错。但是在方法内如果没有调用这个方法,就不会初始化这个变量,编译器就不会报错,在方法里可以先声明后赋值。
final 赋值的时机
在上面上面我们说过final 如果修饰的是成员变量,那么已申明就要赋值,还有其他的赋值方式吗?
- 在构造代码块中
- 构造函数中
final int a;
{
a = 19;
}
/* public Test316() {
super();
a = 10;
// TODO Auto-generated constructor stub
}*/
这两种方式都说明了,在这个类对象建立前,final就必须将值赋值完毕,除了以上三种,其他地方的赋值,均不可以。
final类型转化问题
byte b1=1,b2=2,b3,b6,b8;
final byte b4=4,b5=6,b7;
b3=(b1+b2); //所有的byte,short,char型的值将自
//动被提升为int型,所以接收变量应该是int类型,或者强转成byte
b6=b4+b5; //**被fianl修饰的变量不会自动改变类型**,
//当2个final修饰相操作时,接收变量类型可以是任意类型
b8=(b1+b4); //虽然b4是final修饰,但是b1还是会自动转成int类型
b7=(b2+b5); //结果同上面
final细节
- final 可以修饰类,代表该类不可以继承。(并不是说该类不可以构造对象)
- final 修饰的方法不能被重写,但是可以重载。
- final修饰的属性,代表是常量,在第一次被赋值后不可再更改值。
- 在final中修饰方法的时候,在该方法中不请求一定是final定义的变量哦。
- final修饰的属性代表常量,常量一般在定义的时候就确定值,但是你可以在构造函数中赋值,但是不可以在其他地方赋值
- final 修饰引用变量是引用指向不可变,变量的值是可改变。
- final类的方法能否被同一个包的类访问不是由final决定。
- abstract不能与final同时修饰一个类。抽象本来就是让别人去实现,而你在定义成final不让别人去继承,你搞个毛。
final StringBuffer sb = new StringBuffer("adf");
sb.append("sdasdad");//它没有改变引用指向,只是改变的是值
System.out.println(sb);
final String s ="name";
//s="sadas";改变引用指向,编译不通过
局部变量和成员变量重名
JAVA访问变量采用就近原则,如果不加以处理,那么局部变量是有优先权的。
对于这种重名问题,最简单的问题就是我们通过更换名字就好了。但是,有时也要装逼一下,我就是不换,该怎么办。
private int a = 8;
public static void main(String[] args) {
new Test145().show();
show1();
}
public void show() {
int a = 0;
System.out.println(this.a);//8
System.out.println(a);//0
}
public static void show1() {
int a = 1;
System.out.println(new Test145().a);//8
System.out.println(a);//1
}
上述的代码,一个是非静态方法内,局部变量和成员变量重名,可以在非静态方法中,通过this,来获取对象的成员变量。但是在静态方法内,this不能和static共用,所以通过构造类的对象,来调用成员变量。
Java 内部类外部类文件名
当编写一个java源代码文件时,此文件通常被称为编译单元(有时也被称为转译单元)。每个编译单元都必须有一个后缀名.java。通常我们创建一个类,会自动帮我们创建一个和文件名一致的class,该class修饰是public。这里一个Java源程序中class可以不要public修饰,但是不能用其他修饰,除了only public, abstract & final 。当我们在该类中在申明一个内部类,这时需要注意,内部类除了不能和源程序的文件名一样,其他你怎么都行,也可以用public, abstract & final 以及public修饰。还有一种情况,在定义一个外部类的时候,首先要保证不能和外部类同名,其次,一个源程序文件,只能有一个public修饰,所以,外部类不能在用public修饰,其他的你随意。
可以看到,它把含有public的class 编译成一个class文件,以及该编译单元下的外部类。编译的文件名就是class 的名字。
把内部类编译成一个新的class文件,命名如下,最长的那个。
数组
在java中,数组是引用类型。
数组是引用类型
在java中,数组是引用类型,把数组作用形参传入,并修改值,是影响其原来的值。
数组初始化
数组无论是在定义为实例变量还是局部变量,若没有初始化,都会被自动初始化
static int[] a = new int[5];
static float[] a1 = new float[5];
static double[] a2 = new double[5];
static char[] a3 = new char[5];
static String[] a4 = new String[5];
public static void main(String[] args) {
/* System.out.println(a[0]);//0
System.out.println(a1[0]);//0.0
System.out.println(a2[0]);//0.0
System.out.println(a3[0]);//空格
System.out.println(a4[0]);//null
*/
show();
}
public static void show() {
int[] a = new int[5];
float[] a1 = new float[5];
double[] a2 = new double[5];
char[] a3 = new char[5];
String[] a4 = new String[5];
System.out.println(a[0]);//0
System.out.println(a1[0]);//0.0
System.out.println(a2[0]);//0.0
System.out.println(a3[0]);//空格
System.out.println(a4[0]);//null
}
数组申明
有两种方式
int a[] = new int[5];//很少用,但是要知道
int[] a1 = new int[4];
二维数组
二维数组还是要把它看成一维数组去理解,实际上二维数组的每一行就是一个一维数组
int a[][] = new int[5][3];
int[][] a1 = new int[4][4];
int[] a2 = new int[4];
int a3[] = new int[5];
int a4[][] = new int[3][];//这种行数不确定,可以采用,先通过二维数组,确定几行,在构造每一行的一维数组
a4[0] = new int[5];//代表第一行5列
//int a5[][] = new int[][4];//这样编译不给通过
int a6[][] = {{4,5,6,67},{2,4,5,6}};
int[][] a = {{123},{4,5,6},{8,9,0,7}};
System.out.println(a.length);//3 求的是二维数组的长度,也就是有几个一维数组
System.out.println(a[0].length);//3 第一行一维数组的长度
数组的内存模型
申明的数组变量在栈中,(实际内容或者对象)存放在堆中。
int[] a = new int[3];//在堆中放3个基本类型的数据
Person1[] p = new Person1[5];//堆中放了5个Person1的引用类型对象
Java 命令行操作
首先你要确保你电脑装了Jdk和配置好了环境变量。其次就简单,通过命令进入你存放的Java文件的上级目录。
这里有几点,在当前c盘,如何切换到d 盘,或者其他盘。
在一个分区盘中进入文件目录,直接赋值地址栏的地址
在地址前面加上 cd 空格
经过上面的步骤,你已经进入java文件的上层目录,接下来就是两个命令 javac 和java。
javac 用于编译java源程序的。
java 用于来运行java程序的。
为啥我便以这么简单的类,还出现问题,这里就是告诉我们,通过命令行,你只是编译一个文件,而你这里用到这么多类,你必须提前先编译好,然后,才没有问题,因为你用惯了ide,它是会编译整个项目下的,所以不会出现这个问题的。
另外,需要注意的就是我们在ide下,写的java代码,会自动导包,在用命令行编译、运行会出现问题,最好在源问题,删除导的包的语句,就可正确运行。
javac.exe是编译功能javaCompiler
java.exe是执行class
javac //在cmd输入这个命令,可以查看编译的参数配置
java//在cmd输入这个命令,可以查看运行的参数配置
javac Xxx.java//编译
java Xxx//运行
javac -d <目录> 源文件
-d 目录设置类文件的目标目录。如果某个类是一个包的组成部分,则 javac 将把该类文件放入反映包名的子目录中,必要时创建目录。例如,如果指定 -d c:\myclasses 并且该类名叫 com.mypackage.MyClass,那么类文件就叫作 c:\myclasses\com\mypackage\MyClass.class。
若未指定 -d 选项,则 javac 将把类文件放到与源文件相同的目录中。
java Xxx arg0 arg1 //java 运行携带的参数
值计算
010 = 1*8的1次方 = 8,0开头的是八进制数,需要注意前面的0,是为了和十进制区分用的,也叫转译符。
0x8 = 8*16的0次方 = 8,0x开头的是16进制的数。
System.out.println(1/2);//0
System.out.println(1/2.0);//0.5
在运算中,两个整数相除,结果还是整数,但是如果其中有一个数是浮点类型,其结果就是浮点值。
System.out.println(100%3);//1
System.out.println(100%3.0);//1.0
多种混合计算时,自动将所有数据类型转换为容量最大的一种数据类型。
整数和整数相除,得到的还是整数,原浮点数会强转为int,截断小数部分。
在java中取余是不可以用浮点数,虽然编译是不会出错,但是计算结果是有问题的。
java 算术运算的优先级
单目乘除为关系,逻辑三目后赋值。
单目:单目运算符+ –(负数) ++ – 等
乘除:算数单目运算符* / % + -
为:位移单目运算符<< >>
关系:关系单目运算符> < >= <= == !=
逻辑:逻辑单目运算符&& || & | ^
三目:三目单目运算符A > B ? X : Y
后:无意义,仅仅为了凑字数
赋值:赋值=
本地方法
一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。
抽象类
- 抽象类可以包括抽象方法和非抽象方法
- 一个类用abstract修饰,它可以没有抽象方法,但是如果一个类只要有一个抽象方法,那必须将该类申明成抽象类。
- 如果一个类继承自抽象类,就必须实现它的所有的抽象方法,非抽象方法,看你需要,不请求,如果你不实现抽象方法,就必须把类申明成抽象类。
- 抽象类一般用来就是定义一些通用的东西,所以不允许使用private去修饰。
- 当类是一个抽象类的子类,并且不能为任何抽象方法提供任何实现细节或方法体时,可以选择继续将该类申明成抽象的。
- 抽象类是可以申明定义对象,但是无法实例对象,实例必须通过其实现类。
- 抽象类是可以定义构造方法,如果没有会默认提供一个,这点和类一致的,只是不能直接创建抽象类的实例对象,但实例化子类的时候,就会初始化父类,不管父类是不是抽象类都会调用父类的构造方法,初始化一个类,先初始化父类。
- 抽象类的方法不能用private、static、synchronized、native,象方法没有方法体,是用来被继承的、重写的,所以不能用private修饰;之前就讲过,抽象方法是设计让人重写,static修饰方法,不可以被重写;synchronized关键字是为该方法加一个锁,如果是修饰实例方法。则锁的是当前变量。但是抽象类不能实例化对象,哪有对象一说。
StringBuilder、StringBuffer
- 这两个类是可变字符串,而String是不可变字符串。
- StringBuffer和StringBuilder的默认大小为16。
- StringBuffer 字符串变量(线程安全)StringBuilder 字符串变量(非线程安全)
- 如果程序不是多线程的,那么使用StringBuilder效率高于StringBuffer(线程安全的,肯定效率慢喽)
String
字符拼接
我们都知道在java中字符串的拼接,会想到+,但是需要注意下面的:
System.out.println(1+"1"+2);//112
System.out.println(1+2+"1");//31
如果+的两边出现了字符串,那么字符串前后的将拼接,其他地方的,如果是算术运算,你只管计算
String 判断是否同一个对象
String s = "hello";
String s1 = "hello";
System.out.println(s==s1);//true,由于字符常量区有,所以指向是同一个对象。
String s2 = new String("hello");
String s3 = new String("hello");
System.out.println(s2==s3);//false
System.out.println(s2==s);//false
需要注意:申明一个字符串有两种方式:
- String s = “hello”; 这种方式每次在申明的时候首先先去看字符常量区,有没有有就指向该实例,没有则创建。
- String s2 = new String(“hello”); 这种方式就是构造字符对象,就不光你有没有了,直接创建对象。
window下的盘符
在window下的路径都是以\来分隔的
D:\我的文档\Tencent Files\All Users
但是在Java编程中\是特殊的字符,需要转义。
"c:\\my\\1.txt" "c:/my/1.txt" 都是正确的答案。
反射
- java的封装性:指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,通过该类提供的方法实现对内部信息的操作访问。
- 反射机制:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性
所以说反射是破坏的是封装性。
反射获取类
Class c = Class.forName("com.example.test.Test258");//通过类的全称名
Class cl3= Test.class;//通过类名.class
Class cl2=test.getClass(); //通过类的对象来获取
在上述获取的Class,均是类,而不是一个具体的实例,这点需要强调。
通过反射来修改String的值
我们都知道String指向的内容是无法改变的,但是可以通过反射去修改。
private final char value[];
打开String类的实现,我们发现String类底层是通过char字符去存储,并且是通过private、final修饰,所以外部类是无法去访问的。
String s="helloworld";
Class c = String.class;
Field fieldvalue =null;
try {
fieldvalue = c.getDeclaredField("value");
fieldvalue.setAccessible(false);
char[] value = (char[]) fieldvalue.get(s);
value[5]='*';
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(s);
}
这其中有一个要注意的地方就是关于获取到字段,字段有一个方法setAccessible,该方法的作用是值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。 它会根据你的修饰去检查,是不是允许其他人访问,否则会报以下的异常。
类型的向上、向下转型
//Test253继承于Test252
//Test254继承于Test253
//我们可以得到的是 Test253也是Test252子类
Test252 t = new Test254();//向上转型
Test252 t1 = new Test253();//向上转型
//上面这两个看上去像多态,父类的引用指向子类的对象,之所以可以多态,
//是继承给予了条件,可以向上转型
Test253 t3 = (Test253)new Test252();//向下转型
多态
我们知道多态是面向对象的特征之一,多态是同一个行为具有多个不同表现形式或形态的能力。简单来说就是:父类的引用指向子类对象。
把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码。
多态实现的主要是靠继承和重写。和重载没有半毛钱关系。
多态可以存在于继承和接口中。
public abstract class Test264 {
abstract void show();
}
public class Test265 extends Test264{
@Override
void show() {
// TODO Auto-generated method stub
System.out.println("我是子方法");
}
public static void main(String[] args) {
Test264 t = new Test265();
t.show();//这里的show方法会根据实现类去找
}
}
接口中也类似。
我们将上述的例子,做一些改动:
父类不是一个抽象的,它有自己的方法。
public class Test264 {
public void show() {
System.out.println("这是父类的show方法");
}
}
public class Test265 extends Test264{
@Override
public void show() {
// TODO Auto-generated method stub
System.out.println("这是子类的show方法");
}
public static void main(String[] args) {
Test264 t = new Test265();
t.show();//调用的是Test264的方法
((Test265)t).show();//调用的是Test265
//由于多态是依赖于继承,可以通过向下转型调用子类的方法。
}
我们都知道多态是基于继承和重写,当我们定义一个父类变量指向子类对象的时候,从执行的结果看,好像我们可以通过父类变量去调用子类重写的方法,实际上,是根据动态多态性,到运行的时候才真正绑定真正对象的的方法(这个方法是子类重写的方法,其他的方法是不可以的)
public class Test305 {
public void show() {
System.out.println("我是父类的show方法");
}
}
public static void main(String[] args) {
Test305 t = new Test306();
t.show();
//父类对象无法使用子类对象的方法
//t.show2(); 编译错误
}
@Override
public void show() {
// TODO Auto-generated method stub
System.out.println("我是子类show的方法");
}
public void show2() {
System.out.println("我是子类的show2方法");
}
注解
@SuppressWarnings(“deprecation”)//suppress 的中文意思是阻止的意思 deprecation 在java是指过时
在java 使用过时方法,编译器会提出警告,可以使用该注解取消过时警告。
&&、&、||、|
首先先介绍就是逻辑运算:这种运算是基于符号左右是个表达式,其整个式子的值是true或者false。
而对于&、|,如果符号左右两边是数,这时的比较就是按位运算,一位一位,最终结果还是一个数值。(比较的时候位数不够的补0)
- && 短路逻辑与,两边是boolean类型,一假即假,全真为真,如果式子前面有一个为假,后面就不执行,直接推出式子的结果为假。
- & 非短路逻辑与,两边是boolean类型,一假即假,全真为真,会判断整个式子,最终的结果。
- ||短路逻辑或,两边是boolean类型,一真即真,如果式子前面有一个为真,后面就不执行,直接推出式子的结果为真。
-
||非短路逻辑或,两边是boolean类型,一真即真,会判断整个式子,最终的结果。
& 按位与 有0为0,全1为1。
| 按位或 有1为1,全0为0。
^位异或 相同为0,不同为1。
如何根据等式去计算相应的进制数
13*14=204
(1*n+3)*(1*n+4)=2*n^2+4;// n=-1或n=8; 其中n=8成立
java的跨平台性
JAVA为什么能跨平台?java编译生成的字节码,因为字节码是在虚拟机上运行的,而不是编译器。换而言之,是因为JVM能跨平台安装,所以相应JAVA字节码便可以跟着在任何平台上运行。只要JVM自身的代码能在相应平台上运行,即JVM可行,则JAVA的程序员就可以不用考虑所写的程序要在哪里运行,反正都是在虚拟机上运行,然后变成相应平台的机器语言,而这个转变并不是程序员应该关心的。
构造函数
- 构造方法一定要与定义为public的类同名 。
- 构造方法不能被对象调用,只会创建对象,使用new关键字 。
- 构造方法没有返回值 。
- 构造方法可以进行重载,但是参数列表必须不相同。
- 构造对象,并不一定需要一定要调用构造函数,构造函数也可以写在方法中,进行构造。
- 构造函数在某些特定的情况下,修饰符是可以用private修饰的。
- 构造函数不一定必须在new的情况下调用,可以通过this、super关键字去调用。
- 构造函数在继承的过程中是不可以被继承,子类只能显式或者隐式调用
-构造函数,不可以用synchronized修饰,编译不通过。
(构造方法每次都是构造出新的对象,不存在多个线程同时读写同一对象中的属性的问题,这是针对于实例变量,对于类变量,类变量是共享的,多线程访问肯定会出错,这时我们采用的就是通过synchronized代码块去同步了)
Object
String s = new String("liu");
Object s2 = new Object();
s2 = "liu";
System.out.println(s2.getClass());
System.out.println(s2.equals(s)+"ccc");//true
这个例子让我重新认识Object的赋值,Object是任何类的基类,也就意味着你可以把任何东西赋值给它,赋值Object对象就变成相应赋值的类型。
上面这个例子,一开始我认为s2 类型是Object的,会调用object的equals方法,结果一直想不通。
重写、重载
方法的重载
方法重载的定义:同一个类或与他的派生类中,方法名相同,而参数列表不同的方法。其中参数列表不同指的是参数的类型,数量,类型的顺序(这里必须是不通的参数之间)这三种至少有一种不同。(必须满足这三种之一)
注意重载如果是参数名不一样(也就是形参的名字),是没有用的,它实际上还是同一个,值不过换了一个马甲。
方法重载与下列无关:
与返回值类型无关;与访问修饰符无关(重载的返回值类型,不要求是子类和父类的关系哦。修饰也不要求比父类高,这点和重写不一样)
构造方法也可以重载
方法的重写
方法的重写的定义:在继承关系的子类中,定义一个与父类相同的方法
判断是否重写的方式:在方法之前加上@Override
方法重写的特点:
在继承关系的子类中重写父类的方法
重写的方法必须外壳都一样,具体的实现不一样
重写的方法的返回值类型应该与父类中被重写方法的返回值类型相同或是他的子类类型
重写的方法的访问权限应该与父类中被重写方法的访问权限相同或高于它的访问权限
重写的方法不能抛出比父类更加宽泛的异常
方法重写的注意事项
- private修饰的成员方法不能被重写
- static修饰的方法不能被重写
- final修饰的方法不能被重写
java修饰符
private 权限限于同一个类中
default 权限限于同一个包中,即包权限=default权限
protected 权限限于同一个包中,以及不在同一个包中的子类
public 权限在不同包中都可以
public>protected>default>private
外部类
一个java文件可以包含多个class,但是必须包含最多只能有一个class是public修饰的外部类,也可以没有。(public 修饰的外部类,不一定是在最上面,可以在任何位置)
外部类的修饰符
修饰外部类的修饰符只有四种:public、默认(就是什么没有)、abstract、final。
外部类决定不可以用static修饰,如果外部类用static修饰那么,也就意味着,该类随着应用而启动,如果在整个应用中,并没有用到,那么不是这个类一直在内存中,浪费空间。
内部类
内部类的修饰符
基本上四种常见的修饰符都可以。
而为什么内部类可以使用static修饰呢,因为内部类算是类的成员了,如果没有使用静态来修饰,那么在创建内部类的时候就需要先有一个外部类的对象,如果我们一直在使用内部类,那么内存中就会一直存在外部类的引用,而我们有时候只需要使用内部类,不需要外部类,那么还是会浪费内存,甚至会造成内存溢出。使用static修饰内部类之后,内部类在创建对象时就不需要有外部类对象的引用了。
.java源文件
只能有一个与文件名相同的类,可以包含其他类(可以是内部类、外部类)
源文件就是程序员们所编写出来的文件 程序员们能看懂的文件
类文件则是利用java虚拟机生成的编译文件 是用来给机器看的机器语言
这两者都是一种文件, 但表现形式不同
方法
java中方法可以和类名一样,这不影响。
for语句执行流程
int i ;
for( i = 0;i<10;i++) {
System.out.println(i);//打印是0到9
}
System.out.println(i+"kk");//10
}
for是这么执行的,第一次运行是给i赋初值为0,然后判断是否小于10,然后执行代码,再执行i++
注意如果条件换成++i,结果也是一样这里没有赋值,或者引用,先加后加都一样。
序列化
一个对象只要实现了Serilizable接口,这个对象就可以被序列化。
静态数据不能被序列化,因为静态数据不在堆内存中,而是在静态方法区中。
如何将非静态的数据不进行序列化?用transient 关键字修饰此变量即可。
线程安全
线程安全问题都是由全局变量及静态变量引起的。
若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
java注释
- 单行(single-line)–短注释://……
- 块(block)–块注释:/……/ /*
- 文档注释:/*……/(注释若干行,并写入javadoc文档。每个文档注释都会被置于注释定界符)
java 参数的局部优先
局部变量可以和成员变量相同,使用标识符调用时,优先调用近的。
public int a = 1;
public static void main(String[] args) {
int a = 10;
System.out.println("a=" + a);//10
}
java import与package
package com.example.test;//package是代表将你的类放在com、example、test包下
import java.lang.*;//import 是代表你要使用哪个包中的类
另外还需要注意一点:就是package必须放在该类的最前面,它前面不能有任何东西,可以不再第一行。
java.util.List list = new java.util.ArrayList();
在编写java代码的时候,如果写的是类的全名称,可以不需要导入。
导包只可以导到当前层,不可以再导入包里面的包中的类
如:
要导入java/awt/event下面的所有类
import java.awt.event.*//可以
import java.awt.*//不可以,必须要导包到使用的类当前在的包
Object 方法
finalize 方法:当垃圾回收器(garbage colector)决定回收某对象时,就会运行该对象的finalize()方法。
引用类型的赋值
String a = new String("as");
String c = a;
等号在引用类型中是把引用赋值给另一个对象,都指向同一个内存地址。
new的对象,都在堆内存中,不同的引用对象申明指向的是同一个堆空间,就可以说是指向同一个内存地址。
引用就是指向,有了变量名,有了指向,才能找到真实的值。
equals 比较对象的类型转化
Long l = new Long(42L);
long ll = 42L;
l.equals(ll);//程序会自动帮我们把基本类型转化为Long,进行比较,不会编译出错
通过instanceof来看是否是该类的实例。
可以参看jdk的原码
public boolean equals(Object obj) {
if (obj instanceof Long) {
return value == ((Long)obj).longValue();
}
return false;
}
静态和实例的调用关系
private int a;
private static int b = 8;
public static void main(String[] args) {
//System.out.println(a);//静态方法可以不可以访问非静态
System.out.println(b);//静态方法允许访问静态变量
}
public void show() {
show1();//实例方法可以直接访问静态方法
}
public static void show1() {
//静态方法中访问实例方法通过实例对象
Test294 tt = new Test294();
tt.show();
}
总之一句话:非静态可以调用静态,静态不能调用非静态(不管是静态方法还是静态成员,都是类级别存在的,也就是说随着类的加载而加载,所以在静态方法中调用非静态的成员或方法(此时还不存在对象))。还有静态的,也可以绕个远,通过对象调用,也ok。
注意,静态是不可以直接访问非静态变量,但是是可以在静态访问中定义静态变量哦,这点,你不要搞乱了。
public static void main(String[] args) {
double x = 2.0;
}
java 跨平台
字节码技术和JVM虚拟机技术用于支持java的平台无关性。
java 虚拟机
java 虚拟机,对于方法的调用采用的是栈帧(方法调用和方法执行),调用则入栈,完成之后则出栈。
方法签名
签名是啥意思,就是能够唯一标识该方法。
方法声明的两个组件构成了方法签名 - 方法的名称和参数类型。
java集合
java容器存放的是对象还是引用
java 容器中实际存放的是引用。
List l = new ArrayList<>();
StringBuffer sb = new StringBuffer("aa");
l.add(sb);//是不添加的引用变量名,而并不是aa
有人会说下面,这个是存的值:
l.add(1);
实则集合容器会进行包装,包装成Integer对象的引用。
运算优先级
单目>算数运算符>移位>比较>按位>逻辑>三目>赋值
java
标识符有如下命名规则:
- 由26个英文字母大小写,数字:0-9 符号:_ $ 组成
- 标识符应以字母、_ 、$开头。
- 标识符不能是关键字。
- Java中严格区分大小写
局部变量
定义在方法中变量,我们称之为局部变量。
对于局部变量:
- 局部变量必须要初始化,否则编译不会通过。(全局变量,如果是基本类型,数值默认为0,浮点默认0.0,如果是引用类型,默认为null)
- 局部变量没有必要加修饰符,它只在方法中混,因为方法结束后,局部变量也就gg,有啥意思。
java中赋值坑
int x=3;
int y=1;
if(x=y)//这里编译不会通过,if后面要boolean类型,这里不同于c语言,在c中,只要大于0,就是真,注意一下。
System.out.println(“Not equal”);
else
System.out.println(“Equal”);
}
形参
形参的生命周期,就是整个方法,方法执行结束后,形参也将消失。
形参只可以通过final 修饰符修饰,其他的一律不可以。
null的作用
- 判断一个引用类型数据是否null。 用==来判断。
- 释放内存,让一个非null的引用类型变量指向null。这样这个对象就不再被任何对象应用了。等待JVM垃圾回收机制去回收。
- 可以把null赋值给任意的引用类型,不可以是基本类型。
- null 可以强转成任意的引用类型
public static void main(String[] args) {
((Test310)null).show();
}
public static void show() {
System.out.println("show");
}
因为本题中show方法是static,通过将null强转成Test310类型,可以调用没问题,如果去除static,调用show方法,就需要对象,找不到对象,就报空指针异常喽。
Math类中方法
下面我们记忆方法:数值大的在上面,数值小的在下面。
- ceil 英文有天花板的意思,就是往上看,比它大的,最接近的整数。
- floor 英文有地板的意思,就是往下看,比它小的,最接近的整数。
double d = Math.ceil(11.2);//比11大,最接近的整数12
System.out.println(d);//12
double d1 = Math.ceil(11.5);//比11 大,最接近的整数12
System.out.println(d1);//12
double d2 = Math.ceil(-11.2);//比-11.2大,最接近的整数
System.out.println(d2);//-11
double d3 = Math.ceil(-11.8);//比-11.8 大,最接近的整数
System.out.println(d3);//-11
double d4 = Math.floor(11.2);//比11.2小,最接近的整数
System.out.println(d4);//11
double d5 = Math.floor(11.8);//比11.8 小,最接近的整数
System.out.println(d5);//11
double d6 = Math.floor(-11.2);//比-11.2小,最接近的整数
System.out.println(d6);//-12
double d7 = Math.floor(-11.8);//比-11.8 小,最接近的整数
System.out.println(d7);//-12
- round 该方法是用于四舍五入,你可能觉得很简单,但是负数,你可就不理解了。有的人说把负数先变成正数在四舍五入,最后添加负号。
double d13 = Math.round(-11.5);
System.out.println(d13);//-11
我也是百度,才知道,java中的四舍五入,不管正数还是负数,我们采用的都是先将该数+0.5 然后在floor得出的。
自增
i++
++i
单独的这两句没有什么不同,都是加1。
int j,i =0;
j = ++i;
System.out.println("j"+j+" "+"i"+i);//1 1
j = i++;
System.out.println("j"+j+" "+"i"+i);// 0 1
这里面就存在先赋值后加1,和先加1后赋值。
int sum = 0;
for(int i = 0;i<5;i++) {
sum=sum++;
//这个先将count这个值0暂存起来,然后count自加1变成1,最后将暂存的值赋值给count
}
System.out.println(sum);//0
System.out.println(sum);//如果上面是sum=++sum; 5
double x = 2.0;
int y = 4;
System.out.println( x/=y++);//0.5
System.out.println( x/=++y);//0.4
这种计算中,自增先后,也是会对结果产生影响。
Switch选择判断
switch 支持 int及以下(char, short, byte),String, Enum ,不支持浮点类型。
这个经常就会出现没有break,如果值匹配对应的case,但是执行完成之后发现没有break,它会一直执行下去,这里注意:这里是要进入某个case之后,如果在之前的某个case中,没有break,是不会执行到的。
switch (x)
{
case 1: System.out.println("Test1");
case 2:
case 3:
System.out.println("Test2");
break;
default:
System.out.println("Test3");
break;
}
比如是2,则输出 test2 ,没有test1。