JavaSE知识点总结
关键字:static
- 当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用
- 我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量
-
static关键字使用范围
- 在Java类中可用修饰属性、方法、代码块、内部类 被修饰后的成员具备以下特点
-
随着类的加载而加载
优先于对象存在
修饰的成员,被所有对象所共享
访问权限允许时,可不创建对象,直接被类调用
类属性
- 静态变量存储在方法区中,和具体对象无关
class Person {
// 静态变量total
public static int total = 0;
// 构造方法
public Person() {
total++;
}
}
class StaticDemo {
public static void main(String[] args) {
// 不用创建对象就可以访问静态成员
Person.total = 100;
// 访问方式:类名.类属性 类名.类方法
// 100
System.out.println (Person.total);
Person c = new Person();
// 通过对象访问静态变量
// 101
System.out.println(c.total);
}
}
-
类方法
-
1.类没有实例对象时,可以用
类名.方法名的形式访问由static修饰的类方法
2.在static方法内部只能访问类的static修饰的属性或方法,不能访问类的非static的结构
3.因为不需要实例就可以访问static方法,因此static方法内部不能有this和super(static修饰的方法体属于类本身,this限定符代表的是当前类的实例,supper限定符代表的是当前类的父类的实例)
4.static修饰的方法不能被重写
class Person {
public static void m1(){
m2();
System.out.println("m1");
}
public static void m2(){
System.out.println("m2");
}
}
class StaticDemo {
public static void main(String[] args) {
//m2
//m1
Person.m1();
}
}
单例设计模式
-
设计模式
- 大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模式免去我们自己再思考和摸索。就像是经典的棋谱,不同的棋局,我们用不同的棋谱 单例设计模式
-
1.采取一定的措施保证在整个的软件系统中,对某个类只能存在一个对象实例
2.如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为 private,这样就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象
单例模式优点
- 单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决 单例设计模式应用场景
- 网站计数器、应用程序的日志应用、数据库连接池、读取配置文件的类、任务管理器、回收站
/**
* 饿汉式
*/
class Singleton {
// 私有构造器
private Singleton() {
}
//内部提供一个当前类的实例
public static final Singleton single = new Singleton();
}
/**
*懒汉式
*/
class Singleton {
// 私有构造器
private Singleton() {
}
//内部提供一个当前类的实例
private static Singleton single;
//提供公共的静态的方法,返回当前类的对象
//存在线程安全问题
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
理解main方法的语法
- 由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须是 static的,该方法接收一个String 类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数
- 因为main()方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员
类的成员之四:代码块
-
代码块作用
- 在类加载时自动执行,只执行一次,可多个,加载优先级高于main方法,主要完成初始化操作,例如解析XML文件 代码块的分类
-
一个类中代码块若有修饰符,则只能被static修饰
被static修饰称为静态代码块(static block)
无修饰的称为非静态代码块
静态代码块:用static修饰的代码块
-
1.可以有输出语句
2.可以对类的属性、类的声明进行初始化操作
3.不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法
4.若有多个静态的代码块,那么按照从上到下的顺序依次执行
5.静态代码块的执行要先于非静态代码块
6.静态代码块随着类的加载而加载,且 只执行一次
public class A {
static {
// 语句
}
}
-
非静态代码块:没有static修饰的代码块
-
1.可以有输出语句
2.可以对类的属性、类的声明进行初始化操作
3.除了调用非静态的结构外,还可以调用静态的变量或方法
4.若有多个非静态的代码块,那么按照从上到下的顺序依次执行
5.每次创建对象的时候 都会执行一次,且 先于构造器执行
public class A {
{
// 语句
}
}
程序中成员变量赋值的执行顺序
关键字:final
-
Java中声明类、变量和方法,可使用关键字final来修饰,表示最终的
-
1.
final标记的类不能被继承,提高安全性,提高程序的可读性
2. final标记的方法不能被子类重写,例如Object类中的getClass()
3. final标记的静态变量、成员变量或局部变量称为静态常量、成员常量和局部常量,final标记的成员变量必须在 声明时 或在每个 构造器中 或 代码块中 显式赋值然后才能使用,名称大写,且只能被赋值一次
4.final修饰的引用不能改变,无法被垃圾回收
final class B{
//常量
public final int I = 1;
public final void m1() {
}
public void m1(final int i) {
}
}
抽象类与抽象方法
/**
* 抽象类
* final 和 abstract 不能同时使用
*/
[修饰符列表] abstract class 类名{
类体;
}
abstract class B {
//抽象方法
//只有方法的声明,没有方法的实现,以分号结束
abstract void method1();
}
- 用abstract关键字来修饰一个类,这个类叫做抽象类
- 用abstract修饰一个方法,该方法叫做抽象方法
- 抽象类中有构造方法
- 含有抽象方法的类必须被声明为抽象类
- 抽象类不能被实例化,抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类
- 不能用abstract修饰变量、代码块、构造器
- 不能用abstract修饰私有方法、静态方法、final修饰的方法、final修饰的类
-
抽象类应用
-
模型化那些 父类无法确定全部实现,其子类提供具体实现 的对象的类
超类声明一个方法但不提供实现,该方法的实现由子类提供,这样的方法称为 抽象方法
有一个或更多抽象方法的类称为 抽象类,当然抽象类中也可能没有一个抽象方法
模板方法设计模式
- 抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式
-
解决的问题
-
当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现
换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现,这就是一种模板模式
接口(interface)
[修饰符列表] interface 接口名{
常量;
抽象方法;
}
- 有时必须从几个类中派生出一个子类继承它们所有的属性和方法。但是Java不支持多重继承。有了接口就可以得到多重继承的效果
- 有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘等都支持USB连接
- 接口就是规范
- 接口(interface)是抽象方法和常量值定义的集合
- 接口经过编译也生成.class字节码文件
- 接口之间继承使用extends关键字
- 接口和接口之间强转可以没有继承关系,但可能报ClassCastException
- 类强转成接口也可以没有继承关系(java语法允许)
- 接口解决调用者和实现者之间的耦合
-
接口特点
-
用interface来定义
接口中的所有成员变量都默认是由 public static final修饰的
接口中的所有抽象方法都默认是由 public abstract修饰的
接口中没有构造器
接口采用多继承机制
- 定义Java类的语法格式:先写extends,后写implements
- class SubClass extends SuperClass implements InterfaceA{}
- 一个类可以实现多个接口,接口也可以继承其它接口
- 实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化,否则,仍为抽象类
- 与继承关系类似,接口与实现类之间存在多态性
- 接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和抽象方法的定义(JDK7.0 及之前),而没有变量和方法的实现
-
Java8中关于接口的改进
-
1.Java8中,可以为接口添加
静态方法和
默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念
2.静态方法:使用 static关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类
3.默认方法:默认方法使用 default关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:java8 API中对Collection、List、Comparator等接口提供了丰富的默认方法
interface Filial {
default void help() {
System.out.println("老妈,我来救你了");
}
}
interface Spoony {
default void help() {
System.out.println(" 媳妇,别怕,我来了");
}
}
class Man implements Filial, Spoony {
@Override
public void help() {
System.out.println("我该怎么办呢?");
Filial.super.help();
Spoony.super.help();
}
}
-
接口中默认方法
-
1.若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同参数的方法(不管此方法是否是默认方法),实现类同时实现这两个接口时,会出现接口冲突
解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突(接口中默认方法正常情况下可不重写直接使用)
2.若一个接口中定义了一个 默认方法,而父类中也定义了一个同名同参数的非抽象方法,则不会出现冲突问题。因为此时遵守:类优先原则。 接口中具有相同名称和参数的默认方法会被忽略
接口和抽象类之间的对比
区别点 | 抽象类 | 接口 |
---|---|---|
定义 | abstract修饰的类(可能包含抽象方法) | interface定义的接口(主要是抽象方法和全局常量的集合) |
组成 | 构造方法、抽象方法、普通方法、常量、变量 | 常量、抽象方法、(JDK8:默认方法、静态方法) |
使用 | 子类继承抽象类extends | 子类实现接口implements |
关系 | 抽象类可以实现多个接口 | 接口不能继承抽象类,但允许继承多个接口 |
常见设计模式 | 模板方法 | 简单工厂、工厂方法、代理模式 |
对象 | 通过对象的多态性产生实例化对象 | 通过对象的多态性产生实例化对象 |
局限 | 抽象类有单继承局限 | 接口无此局限 |
实际 | 作为一个模板 | 作为一个标准或一种能力 |
选择 | 抽象类和接口都可以使用的前提下,优先使用接口,避免单继承局限 |
代理模式(Proxy)
- 代理模式是Java开发中使用较多的一种设计模式
- 代理设计就是为其他对象提供一种代理以控制对这个对象的访问
-
应用场景
-
1.安全代理:屏蔽对真实角色的直接访问
2.远程代理:通过代理类处理远程方法调用(RMI)
3.延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象(比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开)
分类
-
1.静态代理(静态定义代理类)
2.动态代理(动态生成代理类),JDK自带动态代理,需要反射等知识
类的成员之五:内部类
-
使用场景
- 当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用 内部类 分类
-
静态成员内部类(内部类可以使用四个修饰符,局部内部类除外)
非静态成员内部类
局部内部类(只能使用默认修饰符)
匿名内部类
- Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类
- 内部类一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称
- 内部类的名字不能与包含它的外部类类名相同
成员内部类
class A{
//静态内部类 创建B类的对象
//A.B b = new A.B();
static class B{
}
}
class A{
//成员内部类 创建B类的对象
//A.B a = new A().new B();
//在类A中调用方法创建
class B{
}
}
-
成员内部类(static和非static)
-
1.可以调用外部类的结构
2.静态内部类不能再使用外层类的非静态的成员变量
3.静态内部类和成员内部类可以在内部定义属性、方法、构造器等结构
4.可以声明为abstract类,因此可以被其它的内部类继承
5.内部类中也可以再声明一个内部类
6.可以声明为final
7.编译以后生成OuterClass$InnerClass.class字节码文件,也适用于局部内部类,不适用于匿名内部类
8.非static的成员内部类中的成员不能声明为static的,只有在外部类或static的成员内部类中才可声明static成员
9.外部类访问成员内部类的成员需要 内部类.成员或 内部类对象.成员的方式
10.成员内部类可以直接使用外部类的所有成员包括私有的数据
11.当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的
12.成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员),如果有相同命名,先访问内部类中的变量和方法
13.可看面试题部分第6题
/**
* 内部类用法举例
*/
class Outer {
private int s;
public class Inner {
public void mb() {
s = 100;
System.out.println("在内部类 Inner 中 s=" + s);
}
}
public void ma() {
Inner i = new Inner();
i.mb();
}
}
class InnerTest {
public static void main(String args[]) {
Outer o = new Outer();
o.ma();
}
}
局部内部类(匿名内部类是局部内部类的一种)
class A{
public void m1(){
//局部内部类的作用范围仅限于方法内,在方法内创建并调用
class B{
}
}
}
-
如何使用局部内部类
-
只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类
它的对象可以通过外部方法的返回值返回使用,返回值类型只能是局部内部类的父类或父接口类型
局部内部类特点
-
1.内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和符号,以及数字编号
2.只能在声明它的方法或代码块中使用,而且是先声明后使用。除此之外的任何地方都不能使用该类
3.局部内部类可以使用外部类的成员,包括私有的
4.局部内部类可以使用外部方法的局部变量,但是必须是final的。由局部内部类和局部变量的声明周期不同所致
5.局部内部类和局部变量地位类似,不能使用public,protected,private
6.局部内部类不能使用static修饰,因此也不能包含静态成员
匿名内部类
- 匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类
- 格式:
new 父类构造器(实参列表) 或 实现接口(){
//匿名内部类的类体部分
}
-
匿名内部类的特点
-
匿名内部类必须继承父类或实现接口
匿名内部类只能有一个对象
匿名内部类对象只能使用多态形式引用
interface A{
void m1();
}
public class B{
public static void main(String[] args){
接口是不能new
//但此处比较特殊是子类对象实现接口,只不过没有为对象取名
new A{
public void m1(){
}
};
}
}
设计模式
面试题
1.抽象类就是比普通类多定义了抽象方法,除了不能直接进行类的实例化操作之外,并没有任何的不同?
- 笔者认为,抽象类有更多的限制,比如类不能用final修饰,客观的答案希望各位大佬补充一下
2.排错
interface A {
int x = 0;
}
class B {
int x = 1;
}
class C extends B implements A {
public void pX() {
// 编译报错
System.out.println(x);
// 1
System.out.println(super.x);
// 0
System.out.println(A.x);
}
public static void main(String[] args) {
new C().pX();
}
}
3.排错
interface Playable {
void play();
}
interface Bounceable {
void play();
}
interface Rollable extends Playable, Bounceable {
Ball ball = new Ball("pingpang");
}
class Ball implements Rollable {
private String name;
public Ball(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void play() {
//问题出现在这里
//在接口中ball为常量,无法修改引用的对象,但可修改引用对象里的值
ball = new Ball("football");
System.out.println(ball.getName());
}
}
4.抽象类与接口有什么区别
5.抽象类是否可继承实体类
- 抽象类可继承实体类,但实体类必须不能是如下两种情况之一:
1.final修饰符修饰的类是不能的
2.如果此实体类有且仅有私有的构造函数也是不能的
6.
public class OuterClass {
private double d1 = 1.0;
//insert code here
}
You need to insert an inner class declaration at line 3. Which two inner class declarations are
valid?(Choose two.)
A. class InnerOne{
public static double methoda() {return d1;}
}
B. public class InnerOne{
static double methoda() {return d1;}
}
C. private class InnerOne{
double methoda() {return d1;}
}
D. static class InnerOne{
protected double methoda() {return d1;}
}
E. abstract class InnerOne{
public abstract double methoda();
}
- 一.静态内部类可以有静态成员和非静态成员,而非静态内部类则不能有静态成员,故A、B错
二.静态内部类的非静态成员可以访问外部类的静态变量,而不可访问外部类的非静态变量,故D错
三.静态内部类的静态成员可以访问外部类的静态变量,而不可访问外部类的非静态变量
四.非静态内部类的非静态成员可以访问外部类的非静态变量,故C正确
五.答案为C、E
7.匿名内部类是否可以继承其它类,是否可以实现接口
- 匿名的内部类是没有名字的内部类。不能继承其它类,但一个内部类可以作为一个接口,由另一个内部类实现
- 上面是给出的标准答案,笔者并不是很明白,只说一下自己的理解,希望大佬们多多指点。
- 因为匿名内部类是类的继承或接口的实现,如果继承的是类则没办法继承其它的类,同时匿名内部类不存在继承其他类和实现其他接口的语法
- 一个内部类可以作为一个接口,由另一个内部类实现
/**
* 不知道这样解释对不对
*/
class A {
abstract class B{
abstract void m1();
}
class C {
public C(B b) {
}
}
}
class D {
public static void main(String[] args) {
A a = new A();
a.new C(a.new B() {
@Override
public void m1() {
}
});
}
}
8.abstract的method是否可同时是static,是否可同时是native(原生的),是否可同时是synchronized
- 都不能,原因是abstract修饰的方法没有方法体,需要子类去重写
9.是否可以通过编译
abstract class Something {
private abstract String doSomething;
}
- 不能通过编译,因为abstract需要子类重写,但是修饰符是私有的,子类无法重写
10.final,finally,finalize的区别
- final是修饰符,是关键字,如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为abstract,又被声明为final。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重写,可以重载
- finally是关键字,异常处理时提供finally语句块来执行任何清除操作。如果抛出一个异常,那么相匹配的catch子句就会执行,之后进入finally语句块
- finalize是Object类中的一个方法名。Java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在Object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的,不要主动调用该方法
11.程序运行结果
public class Test {
public Test() {
Test.Inner s = new Test.Inner();
// 5
System.out.println(s.a);
}
class Inner {
public int a = 5;
}
public static void main(String[] args) {
Test t = new Test();
Inner r = t.new Inner();
// 5
System.out.println(r.a);
}
}
- 能正常运行