同步操作将从 Java精选/Ebooks 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
Java可抛出(Throwable)的结构分为三种类型:被检查的异常(CheckedException),运行时异常(RuntimeException),错误(Error)。
1、运行时异常
定义:RuntimeException及其子类都被称为运行时异常。
特点:
Java编译器不会检查它。也就是说,当程序中可能出现这类异常时,倘若既"没有通过throws声明抛出它",也"没有用try-catch语句捕获它",还是会编译通过。例如,除数为零时产生的ArithmeticException异常,数组越界时产生的IndexOutOfBoundsException异常,fail-fast机制产生的ConcurrentModificationException异常(java.util包下面的所有的集合类都是快速失败的,“快速失败”也就是fail-fast,它是Java集合的一种错误检测机制。当多个线程对集合进行结构上的改变的操作时,有可能会产生fail-fast机制。记住是有可能,而不是一定。
例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生fail-fast机制,这个错叫并发修改异常。Fail-safe,java.util.concurrent包下面的所有的类都是安全失败的,在遍历过程中,如果已经遍历的数组上的内容变化了,迭代器不会抛出ConcurrentModificationException异常。如果未遍历的数组上的内容发生了变化,则有可能反映到迭代过程中。这就是ConcurrentHashMap迭代器弱一致的表现。ConcurrentHashMap的弱一致性主要是为了提升效率,是一致性与效率之间的一种权衡。要成为强一致性,就得到处使用锁,甚至是全局锁,这就与Hashtable和同步的HashMap一样了。)等,都属于运行时异常。
常见的五种运行时异常:
ClassCastException(类转换异常)
IndexOutOfBoundsException(数组越界)
NullPointerException(空指针异常)
ArrayStoreException(数据存储异常,操作数组是类型不一致)
BufferOverflowException
2、被检查异常
定义:Exception类本身,以及Exception的子类中除了"运行时异常"之外的其它子类都属于被检查异常。
特点 : Java编译器会检查它。 此类异常,要么通过throws进行声明抛出,要么通过try-catch进行捕获处理,否则不能通过编译。例如,CloneNotSupportedException就属于被检查异常。
当通过clone()接口去克隆一个对象,而该对象对应的类没有实现Cloneable接口,就会抛出CloneNotSupportedException异常。被检查异常通常都是可以恢复的。 如:
IOException
FileNotFoundException
SQLException
被检查的异常适用于那些不是因程序引起的错误情况,比如:读取文件时文件不存在引发的FileNotFoundException。然而,不被检查的异常通常都是由于糟糕的编程引起的,比如:在对象引用时没有确保对象非空而引起的NullPointerException。
3、错误
定义 : Error类及其子类。
特点 : 和运行时异常一样,编译器也不会对错误进行检查。
当资源不足、约束失败、或是其它程序无法继续运行的条件发生时,就产生错误。程序本身无法修复这些错误的。例如,VirtualMachineError就属于错误。出现这种错误会导致程序终止运行。OutOfMemoryError、ThreadDeath。
Java虚拟机规范规定JVM的内存分为了好几块,比如堆,栈,程序计数器,方法区等。
一个内部类对象可以访问创建它的外部类对象的内容。
如果内部类没有被static修饰,那么它可以访问创建它的外部类对象的所有属性。否则会编译报错:
Cannot make a static reference to the non-static field
静态内部类只能访问静态成员。
如果内部类是被static修饰,即为nested class,那么它只可以访问创建它的外部类对象的所有static属性和static方法。
java中日期格式化使用SimpleDateFormat类操作,比如将2021-11-24字符串格式化成日期类型,需要通过“yyyy-MM-dd”的形式。
但是需要注意的是对于年份来说,大写Y与小写y其实际含义是不同的。
Y代表Week year y代表Year
Week year的含义是当天所在周属于的年份,一周从周日开始,周六结束,那么只要本周跨年,那么这周就算入下一年。
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
calendar.set(2020, Calendar.DECEMBER, 26);
Date strDate1 = calendar.getTime();
SimpleDateFormat f1 = new SimpleDateFormat("YYYY-MM-dd");
System.out.println("Result for YYYY: " + f1.format(strDate1));
SimpleDateFormat f2 = new SimpleDateFormat("yyyy-MM-dd");
System.out.println("Result for yyyy: " + f2.format(strDate1));
}
运行结果:
Result for YYYY: 2021-12-26
Result for yyyy: 2020-12-26
注:格式化使用“yyyy-MM-dd”的形式。YYYY什么的,尽量不要使用。
语法格式:switch(expr)
expr参数可以是一个枚举常量(枚举类由整型或字符类型实现)或一个整数表达式,甚至一个字符串(JDK1.7及以上版本支持String)。
如果是整数表达式可以是基本类型int或其包装类Integer。由于byte、short和char类型都可以隐式转换为int(之前篇幅已阐述过隐式的概念,此处不再过多说明,可关注微信公众号“Java精选”,精品文章每日更新),因此这些类型以及它们对应的包装类都可以作为expr参数。
需要注意的是因为long、float、double等类型都不能够隐式转换为int类型,所以它们不能作为expr参数。如果一定要使用它们,必须将其强制转换为int类型才可以。
在JDK1.7之前版本只能支持byte、short、char、int或者其对应的包装类以及Enum类型;从JDK1.7及以上版本开始支持String类型。
从本质来讲,switch对字符串的支持,其实就是int类型值得匹配。其原理是通过对case后面的String对象调用hashCode()方法,得到一个int类型的hash值,然后用这个hash值来唯一标识这个case。如果可以匹配,接着会调用字符串的String.equals()方法进行判断,若是没有匹配成功,说明不存在。
String变量不能为null,同时switch中case子句中使用的字符串也不能为null。目前为止,switch不支持long类型。
重载(Overload):是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同
重载规则
1、被重载的方法必须改变参数列表(参数个数或类型不一样);
2、被重载的方法可以改变返回类型;
3、被重载的方法可以改变访问修饰符;
4、被重载的方法可以声明新的或更广的检查异常;
5、方法能够在同一个类中或者在一个子类中被重载;
6、无法以返回值类型作为重载函数的区分标准;
实例:
public class OverloadDemo {
// 方法1 无参
public int test() {
System.out.println("公众号Java精选,3000+面试题!");
return 1;
}
// 方法2 单一参数
public void test(int a) {
System.out.println("公众号Java精选,3000+面试题!!");
}
// 方法3 下面两个参数类型顺序不同
public String test(long a, String s) {
System.out.println(s);
return "方法3返回->" + s;
}
//方法4
public String test(String s, int a) {
System.out.println(s);
return "方法4返回->" + s;
}
public static void main(String[] args) {
OverloadDemo od = new OverloadDemo();
System.out.println(od.test());//方法1
od.test(2);//方法2
System.err.println(od.test(3l, "公众号Java精选,3000+面试题!"));//方法3
System.err.println(od.test("公众号Java精选,3000+面试题!!!", 4));//方法4
}
}
执行结果
公众号Java精选,3000+面试题!
1
公众号Java精选,3000+面试题!!
公众号Java精选,3000+面试题!
方法3返回->公众号Java精选,3000+面试题!
方法4返回->公众号Java精选,3000+面试题!!!
公众号Java精选,3000+面试题!!!
非静态的方法可以调用静态的方法,但是静态的方法不可以调用非静态的方法。
类的静态成员(变量和方法)属于类本身,在类加载的时候就会分配内存,可以通过类名直接去访问;非静态成员(变量和方法)属于类的对象,所以只有在类的对象产生(创建类的实例)时才会分配内存,然后通过类的对象(实例)去访问。
在一个类的静态成员中去访问其非静态成员之所以会出错是因为在类的非静态成员不存在的时候类的静态成员就已经存在了,访问一个内存中不存在的东西当然会出错。
1、为了实现字符串池
final修饰符的作用:final可以修饰类,方法和变量,并且被修饰的类或方法,被final修饰的类不能被继承,即它不能拥有自己的子类,被final修饰的方法不能被重写, final修饰的变量,无论是类属性、对象属性、形参还是局部变量,都需要进行初始化操作。
String为什么要被final修饰主要是为了”安全性“和”效率“的原因。
final修饰的String类型,代表了String不可被继承,final修饰的char[]代表了被存储的数据不可更改性。虽然final修饰的不可变,但仅仅是引用地址不可变,并不代表了数组本身不会改变。
为什么保证String不可变呢?
因为只有字符串是不可变,字符串池才有可能实现。字符串池的实现可以在运行时节约很多heap空间,不同的字符串变量都指向池中的同一个字符串。但如果字符串是可变的,那么String interning将不能实现,反之变量改变它的值,那么其它指向这个值的变量值也会随之改变。
如果字符串是可变,会引起很严重的安全问题。如数据库的用户名、密码都是以字符串的形式传入来获得数据库的连接或在socket编程中,主机名和端口都是以字符串的形式传入。因为字符串是不可变的,所以它的值是不可改变的,否则改变字符串指向的对象值,将造成安全漏洞。
2、为了线程安全
因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。字符串自己便是线程安全的。
3、为了实现String可创建HashCode不可变性
因为字符串是不可变的,所以在它创建的时候HashCode就被缓存了,不需要重新计算。使得字符串很适合作为Map键值对中的键,字符串的处理速度要快过其它的键对象。这就是HashMap中的键往往都使用字符串。
while是先执行判断条件,如果条见成立则继续循环,否则直接退出循环。
格式:
初始化条件
while(循环条件){
循环体
迭代部分
}
实例代码如下:
int i=1;
while(i <= 20){
if( i % 2 == 0){
System.out.println(i);
}
do while是先执行一次循环然后在判断while后的条件,如果条件成立则继续循环,否则退出循环。
while和do while的唯一区别就是在条件一开始就不成立时do-while执行一次循环然后退出循环,而while一次循环都不执行就直接退出循环。
格式:
初始化条件
do{
循环体
迭代部分
}while(循环条件);
实例代码如下:
int i=1;
do{
if( i%2 == 0){
System.out.println(i);
}
i++;
}while(i <= 20 );
java在new一个对象的时候,会先查看对象所属的类有没有被加载到内存,如果没有的话,就会先通过类的全限定名来加载。加载并初始化类完成后,再进行对象的创建工作。
我们先假设是第一次使用该类,这样的话new一个对象就可以分为两个过程:加载并初始化类和创建对象。
一、类加载过程(第一次使用该类)
java是使用双亲委派模型来进行类的加载的,所以在描述类加载过程前,我们先看一下它的工作过程:
双亲委托模型的工作过程是:如果一个类加载器(ClassLoader)收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需要加载的类)时,子加载器才会尝试自己去加载。 使用双亲委托机制的好处是:能够有效确保一个类的全局唯一性,当程序中出现多个限定名相同的类时,类加载器在执行加载时,始终只会加载其中的某一个类。
1、加载
由类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部,并存储在运行时内存区的方法区,然后将其转换为一个与目标类型对应的java.lang.Class对象实例
2、验证
格式验证:验证是否符合class文件规范 语义验证:检查一个被标记为final的类型是否包含子类;检查一个类中的final方法是否被子类进行重写;确保父类和子类之间没有不兼容的一些方法声明(比如方法签名相同,但方法的返回值不同) 操作验证:在操作数栈中的数据必须进行正确的操作,对常量池中的各种符号引用执行验证(通常在解析阶段执行,检查是否可以通过符号引用中描述的全限定名定位到指定类型上,以及类成员信息的访问修饰符是否允许访问等)
3、准备
为类中的所有静态变量分配内存空间,并为其设置一个初始值(由于还没有产生对象,实例变量不在此操作范围内) 被final修饰的static变量(常量),会直接赋值;
4、解析
将常量池中的符号引用转为直接引用(得到类或者字段、方法在内存中的指针或者偏移量,以便直接调用该方法),这个可以在初始化之后再执行。 解析需要静态绑定的内容。 // 所有不会被重写的方法和域都会被静态绑定
以上2、3、4三个阶段又合称为链接阶段,链接阶段要做的是将加载到JVM中的二进制字节流的类数据信息合并到JVM的运行时状态中。
5、初始化(先父后子)
1)为静态变量赋值;
2)执行static代码块。
注意:static代码块只有jvm能够调用。如果是多线程需要同时初始化一个类,仅仅只能允许其中一个线程对其执行初始化操作,其余线程必须等待,只有在活动线程执行完对类的初始化操作之后,才会通知正在等待的其他线程。
因为子类存在对父类的依赖,所以类的加载顺序是先加载父类后加载子类,初始化也一样。不过,父类初始化时,子类静态变量的值也有有的,是默认值。
最终,方法区会存储当前类类信息,包括类的静态变量、类初始化代码(定义静态变量时的赋值语句 和 静态初始化代码块)、实例变量定义、实例初始化代码(定义实例变量时的赋值语句实例代码块和构造方法)和实例方法,还有父类的类信息引用。
二、创建对象
1、在堆区分配对象需要的内存:
分配的内存包括本类和父类的所有实例变量,但不包括任何静态变量。
2、对所有实例变量赋默认值:将方法区内对实例变量的定义拷贝一份到堆区,然后赋默认值。
3、执行实例初始化代码:初始化顺序是先初始化父类再初始化子类,初始化时先执行实例代码块然后是构造方法。
4、如果有类似于Child c = new Child()形式的c引用的话,在栈区定义Child类型引用变量c,然后将堆区对象的地址赋值给它。需要注意的是,每个子类对象持有父类对象的引用,可在内部通过super关键字来调用父类对象,但在外部不可访问。
补充:
通过实例引用调用实例方法的时候,先从方法区中对象的实际类型信息找,找不到的话再去父类类型信息中找。
如果继承的层次比较深,要调用的方法位于比较上层的父类,则调用的效率是比较低的,因为每次调用都要经过很多次查找。这时候大多系统会采用一种称为虚方法表的方法来优化调用的效率。
所谓虚方法表,就是在类加载的时候,为每个类创建一个表,这个表包括该类的对象所有动态绑定的方法及其地址,包括父类的方法,但一个方法只有一条记录,子类重写了父类方法后只会保留子类的。当通过对象动态绑定方法的时候,只需要查找这个表就可以了,而不需要挨个查找每个父类。
log4j定义了8个级别的log,除去OFF和ALL可以说分为6个级别。
优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、ALL。
ALL 最低级别,用于打印所有日志记录。
TRACE 很低的日志级别,一般不会使用。
DEBUG 输出细粒度信息事件有助于调试应用程序,主要用于开发过程中打印一些运行信息。
INFO 消息在粗粒度级别上突出强调应用程序的运行过程,打印一些开发者关注的或者重要的信息,用于生产环境中输出程序运行的一些重要信息,但是不能滥用 避免打印过多的日志。
WARN 表示会出现潜在错误的情况,有些信息不是错误信息,但是用于给开发人员的一些提示。
ERROR 指出虽然发生错误事件 但仍然不影响系统的继续运行,打印错误和异常信息 如果不想输出太多的日志 可以使用这个级别
FATAL 指出每个严重的错误事件将会导致应用程序的退出,这个级别是重大错误可以直接停止程序。
OFF 最高等级,用于关闭所有日志记录。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。