文章目录
- 1. static 关键字
- 1.1 static 修饰属性
- 1.2 static 内存存储解析
- 1.3 static 修饰方法
- 1.4 开发中,如何判断是否设置为静态属性或方法
- 2. 单例(Singleton) 设计模式
- 3. Main方法
- 4. 代码块
- 5. final 关键字
- 6. 抽象类 和 抽象方法
- 7. 匿名类
- 8. 模板方法设计模式
- 9. 接口
- 10.接口 匿名类和匿名对象
- 11. 接口应用 代理模式(Proxy)
- 12. 接口应用 工厂模式
- 13. 接口 问题
- 14. JDK8接口 新特性
- 15. 内部类
- 16. 异常 概述 和 体系结构
- 17. Java异常处理的方式
- 18. 手动抛出异常
- 19. 用户自定义异常类
- 20. 异常总结 和 面试题
1. static 关键字
static 静态关键字可以用来修饰:属性,方法,代码块,内部类。
1.1 static 修饰属性
静态变量,相当于共享了这个变量,这样你修改其他的实例对象的这个静态变量,全部的这个静态属性值都会跟着变。
package com.itholmes.p2;
public class StaticTest {
public static void main(String[] args) {
Chinese c1 = new Chinese();
c1.name = "张三";
c1.age = 40;
c1.nation = "chinese";
Chinese c2 = new Chinese();
c2.name = "李四";
c2.age = 20;
c2.nation = "中国";
//由于使用static修饰了nation,因此nation就变成了共享c2的修改,也会对c1修改!
System.out.println(c1.nation);
}
}
class Chinese{
String name;
int age;
static String nation;
}
实例变量是随着类的对象的创建在堆空间中完成的。
而静态变量是随着类的加载而加载完成的。
相比较显然,静态变量要比实例变量先一步创建。
因此,我们可以从一开始不用创建对象,就可以定义静态变量的值。
package com.itholmes.p2;
public class StaticTest {
public static void main(String[] args) {
//因为,静态变量是随着类的加载而完成,因此可以直接定义创建。
Chinese.nation = "中国";
Chinese c1 = new Chinese();
c1.name = "张三";
c1.age = 40;
Chinese c2 = new Chinese();
c2.name = "李四";
c2.age = 20;
System.out.println(c1.nation);
System.out.println(c2.nation);
}
}
class Chinese{
String name;
int age;
static String nation;
}
1.2 static 内存存储解析
其实,将栈,堆,方法区这几个概念和作用理解好,就知道这几个内存解析了。
1.3 static 修饰方法
静态方法和静态变量一样,也是随着类的加载而加载,可以直接通过"类.静态方法"的方式进行调用,而不是先声明对象才能调用。
最重要的是,静态方法中,只能调用静态的方法和属性。
非静态方法中,既可以调用非静态方法和属性,也可以调用静态方法和属性。
package com.itholmes.p2;
public class StaticTest {
public static void main(String[] args) {
Chinese.eat();
}
}
class Chinese{
String name;
int age;
static String ate;
public static void eat() {
System.out.println("吃饭"+ate);
}
}
在静态方法中,不能使用this,super等关键字。
this和super必须基于当前对象或当前对象的父类,连对象都没有创建那就更不用提调用了它们了。
1.4 开发中,如何判断是否设置为静态属性或方法
就是共享不共享,属性都统一,方法也都统一(例如:Math,Arrays等等方法)。
2. 单例(Singleton) 设计模式
单例设计模式:对某个类只能存在一个对象实例!
因此,这个设计模式仅仅适合只有一个对象的场景比较适合使用。
单例的饿汉式设计:
package com.itholmes.p2;
public class SingletonTest {
public static void main(String[] args) {
//4.静态调用的创建Bank对象。
Bank bank1 = Bank.getInstance();
Bank bank2 = Bank.getInstance();
System.out.println(bank1 == bank2);
//这两个共用的一个设计对象
}
}
// 这种方式调用叫做 饿汉式。
class Bank{
//1.私有化类的构造器,私有化目的:就是为了防止外部创建对象。
private Bank() {
}
//2.在内部可以创建类的对象,这里骑士可以看成一个属性
private static Bank instance = new Bank();
//3.提供公共的方法,返回类的对象
public static Bank getInstance() {
return instance;
}
}
单例的懒汉设计:
package com.itholmes.p2;
public class SingletonTest2 {
Order order1 = Order.getInstance();
Order order2 = Order.getInstance();
}
//这种设计模式为懒汉式,其实饿汉式和懒汉式差不多。
class Order{
//1.私有化类的构造器
private Order() {
}
//2.声明当前类的对象
private static Order instance = null;
//3.声明public static的返回当前类对象的方法
public static Order getInstance() {
if(instance == null) {
instance = new Order();
}
return instance;
}
}
单例(Singleton)设计模式的应用场景:
3. Main方法
如果一个类中,有两个main方法一般在运行的时候会提示你选择哪一个main方法作为入口。
package com.itholmes.p2;
public class MainTest {
public static void main(String[] args) {
Main.main(new String[100]);
}
}
class Main{
public static void main(String[] args) {
for(int i=0;i<args.length;i++) {
args[i] = "args_" + i;
System.out.println(args[i]);
}
}
}
一般我们在命令行直接给添加参数,给main方法。eclipse可以使用Arguments来给main方法添加参数。
4. 代码块
代码块的作用就是用来初始化类,对象。
代码块分两种:静态代码块和非静态代码块
静态代码块:
- 内部可以有输出语句(Syso)。
- 静态代码块可以随着类的加载而执行。
- 静态代码块只执行一次!
- 如果一个类定义了多个代码块(静态和非静态),就按张声明的先后顺序执行。因为它是随着类的创建加载而声明。
- 此外,静态代码块要优先非静态代码块,因为静态是随着类的创建加载而产生,后者是因为对象的创建加载。
- 静态代码块只能调用静态属性方法结构,不能调用非静态的结构。
静态代码块作用:初始化一些类的静态属性信息等等。
非静态代码块:
- 内部也可以有输出语句(Syso)。
- 随着类对象的创建而加载执行。
- 每创建一个对象,就执行一次静态代码块。
因为具备上面的这些特点,因此,非静态代码块的功能就是在创建对象时,对对象的属性等进行初始化。
对属性初始化的总结:
最重要的一点,有继承效果的,一定区分好super()和this()的一些调用,以及静态和非静态代码块的执行顺序,前者伴随的类,后者伴随着对象。
5. final 关键字
final 用来修饰类,方法,变量。
本意就是最后一个!
- 被final修饰的类,不能被其他类所继承。例如:String类,System类,StringBuffer类,都是final类。
- 被final修饰的方法,表名此方法不能被重写。例如:Object类中getClass()方法。
- 被final修饰变量(属性),此时这里的变量就称为一个常量,不能改变值了。
final修饰变量,对于赋值对应有:显示初始化,代码块初始化,构造器初始化。
声明了final变量,但是没有赋值,想从调用方法中传递赋值,这是不行的!!
换句话说,final声明的变量,必须要有赋值。
一个小细节:final声明的变量名一般为大写,因为常量么。
形参使用final声明:(其实,就是传递给过来的数据形参也成了不可改变。
static可以修饰属性,方法,代码块,内部类(一定是内部类)。(不能修饰构造器)
final可以修饰类,属性,方法,其他不可以。
6. 抽象类 和 抽象方法
abstract关键字可以用来修饰:类,方法。
抽象类就可以理解为是一个半成类,主要是先定义好一些功能,随后方便继承类使用。
abstract关键字:修饰类(抽象类)
- abstract声明的类不能被实例化(不能创建对象)。
- 抽象类中一定有构造器!虽然自己不能定义对象了,但是子类还是要用的!!!
- 开发中,一定提供抽象类的子类,让子类对象实例化,完成相关操作。
abstract关键字:修饰方法(抽象方法)
- 抽象方法只有方法的声明,没有方法体!!
- 包含抽象方法的类,一定是一个抽象类。反之,抽象类中是可以没有抽象方法。
- 当然,如果父类是抽象类里面还有抽象方法,这样子类要么重写这个抽象方法,要么将子类也定义为abstract抽象类。
package com.itholmes.p2;
public class AbstractTest {
public static void main(String[] args) {
//因为抽象类是不能实例化的
// PersonX p1 = new PersonX();
// p1.eat();
}
}
abstract class PersonX {
String name;
int age;
public PersonX() {
}
public PersonX(String name,int age) {
this.name = name;
this.age = age;
}
public abstract void eat();
public void walk() {
System.out.println("人们走路");
}
}
//要么将该类定义为抽象类(因为父类含有抽象类)
abstract class Student extends PersonX{
public Student(String name,int age) {
super(name,age);
}
//要么重写eat()类
// @Override
// public void eat() {
// System.out.println("学生吃饭");
// }
}
abstract的注意事项:
- abstract不能用来修饰:属性,构造器等结构。
- abstract不能用来修饰私有方法(private不能被重写),静态方法(子父类的同名同参数的静态方法,不能重写!),final的方法和类。
7. 匿名类
对于abstract声明的类,不能创建对象,但是可以创建一个匿名类的对象。
package com.itholmes.p2;
public class AbstractTest {
public static void main(String[] args) {
//这就是一个匿名的子类对象:p
PersonX p = new PersonX() {
@Override
public void eat() {
System.out.println("匿名类创建");
}
};
}
}
abstract class PersonX {
String name;
int age;
public PersonX() {
}
public PersonX(String name,int age) {
this.name = name;
this.age = age;
}
public abstract void eat();
public void walk() {
System.out.println("人们走路");
}
}
8. 模板方法设计模式
其实整体上,就是通过继承外加abstract抽象方法,来实现一种模板方式。
就像下面代码的code()部分一样。
package com.itholmes.p2;
public class TemplateTest {
public static void main(String[] args) {
Template tem = new SubTemplate();
tem.spendTime();
}
}
abstract class Template{
public void spendTime() {
long start = System.currentTimeMillis();
//这里的code代码就是不确定的部分。
this.code(); //this指的就是当前对象,这里指的就是tem,tem.code()是重写后的code方法。
long end = System.currentTimeMillis();
System.out.println("花费的时间为:"+ (end - start));
}
public abstract void code();
}
class SubTemplate extends Template{
@Override
public void code() {
for(int i=2;i<=1000;i++) {
boolean isFlag = true;
for(int j=2;j<=Math.sqrt(i);j++) {
if(i%j ==0) {
isFlag = false;
break;
}
}
if(isFlag) {
System.out.println(i);
}
}
}
}
9. 接口
接口和类是两个并列的结构。
JDK7 以前版本仅仅定义了全局常量和抽象方法:
- 全局常量:public static final的类型,可以省略不写。
- 抽象方法:public abstract的类型,也可以省略不写。
JDK8除了定义全局常量和抽象方法,还定义了静态方法,默认方法。
- 接口不能定义构造器。意味着接口不能实例化。
- Java开发中,接口通过让类去实现(implements)的方法来使用。
- 需要注意的是,如果实现类对应接口有抽象方法,就要对应对抽象方法的重写或将实现类定义为抽象方法。
- 可以implements多个接口,弥补了Java单继承性的局限性。
格式:class 类名 extends 父类 implements 接口1,接口2,接口3 - 接口和接口之间是可以继承的,多继承。
- 接口具有多态性,因此,实现类可以对应接口创建对象。
10.接口 匿名类和匿名对象
主要四种情况:
package com.itholmes.p2;
public class USBTest {
public static void main(String[] args) {
Computer com = new Computer();
//1.创建了接口的非匿名实现类的非匿名对象
Flash flash = new Flash();
com.transferDate(flash);
//2.创建了接口的非匿名实现类的匿名对象
com.transferDate(new Printer());
//3.创建了接口的匿名实现类的非匿名对象
USB phone = new USB() {
@Override
public void start() {
// TODO Auto-generated method stub
System.out.println("手机开始工作");
}
@Override
public void stop() {
// TODO Auto-generated method stub
System.out.println("手机结束工作");
}
};
com.transferDate(phone);
//4. 创建了接口的匿名实现类的匿名对象
com.transferDate(new USB() {
@Override
public void start() {
// TODO Auto-generated method stub
System.out.println("MP4开始工作");
}
@Override
public void stop() {
// TODO Auto-generated method stub
System.out.println("MP4结束工作");
}
});
}
}
class Computer{
public void transferDate(USB usb) {
usb.start();
System.out.println("具体传输数据的细节");
usb.stop();
}
}
interface USB{
void start();
void stop();
}
class Flash implements USB{
@Override
public void start() {
// TODO Auto-generated method stub
System.out.println("U盘开始工作");
}
@Override
public void stop() {
// TODO Auto-generated method stub
System.out.println("U盘结束工作");
}
}
class Printer implements USB{
@Override
public void start() {
// TODO Auto-generated method stub
System.out.println("打印机开始工作");
}
@Override
public void stop() {
// TODO Auto-generated method stub
System.out.println("打印机结束工作");
}
}
11. 接口应用 代理模式(Proxy)
代理模式,就是为其它对象提供一种代理以控制对这个对象的访问。 理解好,代理类和非代理类就可以了。
package com.itholmes.p2;
public class NetWorkTest {
public static void main(String[] args) {
Server server = new Server();
new ProxyServer(server);
}
}
interface NetWork{
public void browse();
}
//被代理类
class Server implements NetWork{
@Override
public void browse() {
// TODO Auto-generated method stub
System.out.println("真实的服务器访问网络");
}
}
//代理类
class ProxyServer implements NetWork{
private NetWork work;
public ProxyServer(NetWork work) {
this.work = work;
}
public void check() {
System.out.println("联网之前的检查工作");
}
@Override
public void browse() {
check();
// TODO Auto-generated method stub
work.browse();
}
}
12. 接口应用 工厂模式
工厂模式:实现创建者与调用者的分离,即将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
就是实现分工的一个效果。
需要注意xxxFactory这个类,一般都是什么工厂,就是一个工厂类,用来造东西的。
简单工厂模式:
工厂方法模式:
抽象工厂模式:
13. 接口 问题
继承和接口同时出现相同变量的问题:
首先,上面的play()方法,虽然有两个play(),但是重写的play()覆盖了接口上面的两个,是没有问题的!
有问题的是:Ball定义在了接口,接口是public abstract final的!!!因此不能被重新赋值!
14. JDK8接口 新特性
接口的静态方法:
- 接口种定义的静态方法只能通过接口来调用。而不能通过实现类来调用。
接口的默认方法:
- 通过实现类的对象,可以调用接口中的默认方法。
- 默认方法可以被重写的。
- 如果子类继承的父类和实现的接口声明了同名同参数的方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。(这就是一个类优先原则)
接口默认方法的接口冲突:
如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么实现类没有重写此方法的情况下会报错!
解决办法:很简单就是重写方法就可以了。
15. 内部类
Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类。
内部类有两种:
- 成员内部类:直接定义在类里面,方法外,构造器外,代码块外的类。
- 局部内部类:方法内,代码块内,构造器内的类。
成员内部类可以做的事情:
创建非静态成员内部类和创建静态成员的内部类形式:
package com.itholmes.p3;
public class InnerClassTest {
//创建PersonX实例(静态的成员内部类)
//静态内部类可以直接定义
Person.PersonX x = new Person.PersonX();
//创建PersonY实例(非静态的成员内部类)
//但是非静态内部类不可以直接定义,因为它必须有一个对象来调用这个内部类才可以
//Person.PersonY y = new Person.PersonY();
Person p = new Person();
Person.PersonY y = p.new PersonY();
}
class Person {
//静态成员内部类
static class PersonX{
}
//非静态成员内部类
class PersonY{
}
public void method() {
//局部内部类(方法内部)
class AA{
}
}
{
//局部内部类(代码块内)
class BB{
}
}
public Person() {
//局部内部类(构造器内部)
class CC{
}
}
}
如何调用外部类,内部类,甚至形参的一些同名属性:
就是使用this和类名.this来调用。
返回一个实现Comparable接口的类的对象的方法:
//方式一:
//返回一个实现Comparable接口的类的对象的方法:
public Comparable getComparable() {
class MyComparable implements Comparable{
@Override
public int compareTo(Object o) {
// TODO Auto-generated method stub
return 0;
}
}
return new MyComparable();
}
//方式二:(匿名)
public Comparable getComparable() {
return new Comparable() {
@Override
public int compareTo(Object o) {
// TODO Auto-generated method stub
return 0;
}
};
}
抽象类和接口的共同点和区别:
在局部内部类的方法中,如果调用局部内部类所声明的方法中的局部变量的话,要求此局部变量必须声明final的(JDK8版本以上,默认就是final!),这就是一个规定。
package com.itholmes.p3;
public class InnerClassTest {
public void method() {
//方法内部的局部变量
int num = 10;
class AA{
public void show() {
//不能修改该num值,会报错!!
//因为这里的num默认是final类型。
//num = 20;
System.out.println(num);
}
}
}
}
以上这种情况,经常遇到匿名内部类的情况,如下:
16. 异常 概述 和 体系结构
堆溢出和栈溢出:
如果遇到这种错误,有两种解决方法:一旦遇到错误就终止程序的运行。另一种方法由程序员编写程序时,就考虑到错误的检测,错误消息的提示,以及错误的处理。
对于异常的一些错误,面试倒是经常提问:
17. Java异常处理的方式
方式一:try-catch-finally
注意:finally,无论出现异常,最后都会执行。像文件关闭,数据库连接,输入输出流,网络编程Socket等资源的关闭(JVM不能自动的回收),需要自己来关闭,这就需要finally来实现了。
需要注意的时候,try中有return语句时,每个代码执行的情况顺序,最注意的一点final是一定执行的!
package com.itholmes.p3;
import org.junit.Test;
public class ExceptionTest {
@Test
public void testMethod() {
int num = test1();
System.out.println(num);
}
public int test1(){
try {
System.out.println("我是开头");
int[] arr = new int[10];
System.out.println(arr[10]);
return 1;
}catch(ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
return 2;
}finally {
System.out.println("我一定会被执行");
return 3;
}
}
}
方式二:throws + 异常类型
注意:异常代码后续的代码,就不再执行!
@Test
public void method2() throws FileNotFoundException,IOException{
File file = new File("hello.txt");
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while(data != -1) {
System.out.print((char)data);
data = fis.read();
}
fis.close();
}
遇到异常解决办法就是这两种。
此外,try-catch-finally真正的将异常给处理掉了。而throws的方式只是将异常抛给了方法的调用者,并没有将异常真正的处理掉。
方法重写的抛出异常规则:
- 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型。
子类重写的方法抛出的异常类型 < 父类被重写的方法异常类型
开发中什么时候用try-catch-finally,什么时候用throws?
- 对于几个方法而言,它们是递进关系,那么最好使用throws来抛异常。
- 对于这几个方法整体而言,可以用try-catch-finally来判定异常和异常处理。
18. 手动抛出异常
异常对象产生两种方式:
- 一种是:系统自动生成的异常对象。
- 另一种是:手动的生成一个异常对象,并抛出(throw)
手动抛出异常对象:throw
注意:要灵活使用getMessage()这个异常信息获取!
package com.itholmes.p3;
public class StudentTest {
public static void main(String[] args) {
try {
Student s = new Student();
s.regist(-1001);
System.out.println(s);
} catch (Exception e) {
// TODO Auto-generated catch block
//e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
class Student{
int id;
//解释一下,为什么加入throws Exception
//被强制异常处理的代码块,必须进行异常处理,否则编译器会提示“Unhandled exception type Exception”错误警告
//进行异常处理就只有throws抛出和try-catch-finally语句两种方式。
public void regist(int id) throws Exception{
if(id >0) {
this.id = id;
}else {
//这里数据异常了就可以手动抛出异常!!
//System.out.println("您输入的数据非法");
//这里抛出了Exception异常,上面就要进行异常处理
throw new Exception("您输入的数据非法!");
}
}
@Override
public String toString() {
return "Student [id=" + id + "]";
}
//有时候为什么RuntimeException不用throws异常
//RuntimeException,也就是运行时异常,表示代码本身存在BUG
}
19. 用户自定义异常类
如何自定义异常类?
- extends继承于现有的异常结构:RuntimeException,Exception。
例如:
package com.itholmes.p3;
public class MyException extends RuntimeException{
//这是RuntimeExcption的一个全局变量
static final long serialVersionUID = -7034897190745766939L;
//重写RuntimeExcption异常类的构造器
public MyException() {
}
public MyException(String msg) {
super(msg);
}
}
20. 异常总结 和 面试题
异常总结:
面试题: