Java基础要点笔记1(尚硅谷)
Java基础要点笔记2面向对象(尚硅谷)
Java编程之异常处理
Java编程之多线程
Java编程之常用类
Java编程之枚举类&注解
Java编程之Java集合
Java编程之泛型
Java编程之IO流
Java编程之网络编程
Java编程之反射机制
Java编程之Java8&9&10&11新特性
十大排序算法
关于设计模式
类就是一个抽象的概念,对象就是一个具体的实例,类成员有类属性和类方法,通过“对象.属性”或“对象.方法”调用对象的结构
注:Person p3 = p1 ; // 将p1变量保存的对象地址赋给p3,导致p1和p3指向堆空间中的同一个对象实体,一个属性的修改,另一个也会进行相应的修改
主要介绍运行时数据区的三个部分:
堆Heap: 此内存区域的唯一目的就是存放对象实例,所有对象实例以及数组都要在堆上分配;
虚拟机栈Stack: 即通常所说的栈,用于存放局部变量等,局部变量表存放了编译器可知长度的各种基本数据类型、对象引用(reference类型,它不等同于对象本身,是对象在堆内存的首地址),方法执行完,自动释放;
方法区Method Area: 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
为了加深理解,再附一张对象数组的内存解析图:
引用类型的变量,只可能存储两类值:null 或 地址值(含变量的类型)
比如Phone的对象p,打印出来的值为
需要注意的是,编译完源程序以后,生成一个或多个字节码文件,我们使用JVM中的类加载器和解释器对生成的字节码文件进行解释运行。意味着,需要将字节码文件对应的类加载解析(是执行java xxx.exe的时候做的事情)
相同点在于定义的格式相同;先声明、后使用;变量都有其对应的作用域。
下面重点介绍一下不同点:
属性:直接定义在类中;
局部变量:声明在方法内、方法形参、代码内、构造器形参、构造器内部的变量;
属性:可以在声明属性时,指明其权限,使用权限修饰符:peivate、public、protected
局部变量:不可以使用权限修饰符
属性:根据其类型,都有默认初始化值
整型(byte、short、int、long) 0
浮点型(float、double) 0.0
字符型(char) 0或’\u0000’
布尔型(boolean) false
引用数据类型(类、数组、接口):null
局部变量:没有默认初始值,调用局部变量之前一定要显示赋值,特别的,形参在调用时赋值即可。
属性:加载到堆内存中(非static)
局部变量:加载到栈空间
注:解释一下堆共享和栈共享,参见https://blog.csdn.net/fox_bert/article/details/88367002,讲的挺清楚
栈是数据共享,堆是线程共享(我理解的数据共享就是在栈中有1个3,然后多个int类型的变量指向它。而对于new出来的对象,比如String,每次new都会在堆中有新的地址,哪怕字符串的内容是一样的。)
数据共享的有栈、常量池、寄存器、PC
线程共享的有堆、全局变量、静态变量、方法区
ctrl+shift+t 可以搜索类文件,进到类文件后 ctrl+o 可以搜索方法
权限修饰符 返回值类型 方法名(形参列表){
// 方法体
}
注:static、final、abstract也可以修饰方法,后面再讲
如何理解“万事万物皆对象”?
在Java语言中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体的功能结构;当涉及到与前端、数据库在java层面交互时,都体现为类、对象。
定义: 在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。(与返回类型无关)
总结: “两同一不同”:同一个类、相同方法名;参数列表不同
jdk1.5提供了Varargs机制,允许直接定义和多个实参相匹配的形参。
这种可变形参的方法可以用在sql语句的where字句那,因为有时候不知道会匹配几个参数。
补充:对于可以自动类型转换的,可以在传参时传入,比如现在实参为int型,但是方法中没有形参为int型,有形参是double,那么也可以传进去。(多个参数也是一样,能自动类型转换,表示能够匹配,我在eclipse中试着是可以的)
关于变量赋值:
值传递机制:
public class Test {
public static void main(String[] args) {
int a = 10 ;
int b = 10 ;
method(a,b) ;
System.out.println("a="+a);
System.out.println("b="+b);
}
}
在不改变原本题目的前提下,如何写这个函数才能在main函数中输出a=100,b=200?
方法一:在method方法中输出,之后System.exit(0) ;
方法二:重置打印流,重写println
第二道题目:微软
定义一个int型的数组:int[] arr = new int[]{12,3,4,5,654,765,54,34,645,32} ;让数组的每个位置上的值除以首位置的元素,得到结果,作为该位置上的数值,遍历新的数组。
解法:倒着遍历
第三道题目:
int[] arr = new int[]{1,2,3} ;
System.out.println(arr) ; //地址值
char[] arr1 = new char[]{‘a’,‘b’,‘c’} ;
System.out.println(arr1) ; //abc
分析:这是跟println方法有关,传入的数组参数都是用Object来接收,但是对于char数组,是直接用char数组来接收的,会打印数组中的所有内容。
一个方法体内调用它自身。
例题:计算1-100之间所有自然数的和
public int getSum(int n){
if(n==1){
return 1 ;
}else{
return n + getSum(n-1) ;
}
}
这里需要注意的是: 代码块先于构造器执行
比如现在Leaf的父类是Mid,Mid的父类是Root,每个类中有静态代码块、非静态代码块,若干构造函数,现在new一个静态Leaf,它们的执行顺序为:
main方法除了是程序的入口之外,也是一个普通的静态方法,也是在执行main方法之前,如果main所在的类中有静态代码块,也会先于main方法
格式:
成员内部类:外部类$内部类.class
局部内部类:外部类$数字 内部类名.class
规定:局部内部类的方法中,如果调用局部内部类所声明的方法中的局部变量,则要求该变量声明为final。jdk7及以前的版本,要求显示声明为final,但是Jdk1.8之后可以省略final的声明(我理解如果被局部内部类调用了,那么在其他地方也不能修改了)
局部内部类不能有访问控制修饰符,且不能用static修饰,但是可以是abstract和final的
需要知道三个问题
// 创建静态成员内部类实例
Person.Dog dog = new Person.Dog() ;
// 创建非静态成员内部类实例
Person p = new Person() ;
Person.Bird bird = p.new Bird() ;
// 内部类中的方法
public void display(String name) {
System.out.println(name) ; // 方法形参
System.out.println(this.name) ; // 内部类的属性
System.out.println(Person.this.name) ; // 外部类的属性
}
问题的引入:
当我们创建一个类的对象以后,我们可以通过“对象.属性”的方式,对对象的属性进行赋值,这里,赋值操作要受属性的数据和存储范围的制约。除此之外,没有其他制约条件。但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件,这个条件就不能在属性声明时体现,我们只能通过方法进行限制条件大的添加。同时为了避免用户再使用“对象.属性”的方式对属性进行赋值,则需要将属性声明为私有的private—>此时针对属性就体现了封装性。
封装性的体现
我们将类的属性xxx私有化private,同时,提供公共的public方法来获取getXxx和设置setXxxx
拓展:封装性的其他体现:比如 不对外暴露的私有方法、单例模式、如果不希望类在包外被调用,可以将类设置为缺省(类只能是public或缺省)等等。
封装性的体现,需要权限修饰符配合
总结:Java提供了四种权限修饰符来修饰类及类的内部结构,体现了其在被调用时的可见性大小。
构造器的作用:
说明:
总结:属性赋值的先后顺序
① 默认初始化
② 显示初始化/③ 在代码块中赋值 (这两个看写的先后顺序)
④ 构造器中初始化
⑤ 通过“对象.方法” 或 “对象.属性”的方式,赋值
拓展知识:JavaBean
① JavaBean是一种Java语言写成的可重用组件
②所谓JavaBean,符合如下标准的java类:
类是公共的
有一个无参的公共的构造器
有属性,且有对应的get、set方法
1. 继承性的好处
① 减少了代码的冗余,提高了代码的复用性
② 便于功能的扩展
③ 为之后的多态性的使用,提供了前提
2. 继承性的格式:class A extends B{ }
A:子类、派生类、subclass
B:父类、超类、基类、superclass
① 体现:一旦子类A继承父类B之后,子类A中就获取了父类B中声明的所有的属性和方法;
特别的,父类中声明为private的属性或方法,子类中继承之后,仍然认为获取了父类中的私有结构。只是因为封装性的影响,使得子类不能直接调用父类的结构而已。
② 子类继承父类之后,还可以声明自己特有的属性或方法:实现功能的拓展
3. Java中关于继承性的规定
① 一个类可以被多个子类继承
② Java中类的单继承:一个类只能有一个父类
③ 子父类是相对的概念
④ 子类直接继承的父类称为直接父类,间接继承的父类称为间接父类
⑤ 子类继承父类之后,就获取了父类以及所有间接父类中声明的属性和方法
4. 类图
eclipse中快捷键ctrl+t可以看到类的继承情况
如果我们没有显示的声明一个类的父类的话,则此类继承于java.lang.Object类。也就是说所有java类(除java.lang.Object类之外)都直接或间接的继承于java.lang.Object类。
所以,所有的java类具有java.lang.Object类声明的功能,后面会对Object进行讲解。
1. 重写: 子类继承父类之后,可以对父类中同名同参数的方法,进行覆盖操作
2. 应用: 重写之后,当创建子类对象之后,通过子类对象调用父类中同名同参数的方法时,实际执行的是子类重写父类的方法。
Person父类:
package com.atguigu.contact;
public class Person {
String name ;
int age ;
public Person(){
}
public Person(String name, int age){
this.name = name ;
this.age = age ;
}
public void eat(){
System.out.println("Person: 吃饭");
}
public void walk(int distance){
System.out.println("Person走路:距离是" + distance + "公里");
// 难点:因为这里父类中的show方法是私有的,所以子类中不能算是重写,所以如果子类对象调用walk方法,这里会调用父类的show方法
System.out.println("下面为调用父类中的show方法");
show() ;
// 难点:因为子类中重写eat方法,所以如果是子类对象调用walk方法,这里,会调用子类的eat方法
System.out.println("下面为调用子类中的eat方法");
eat() ;
}
private void show(){
System.out.println("Person:调用show方法");
}
}
Student子类:
package com.atguigu.contact;
public class Student extends Person{
String major ;
public Student(){
}
public Student(String major){
this.major = major ;
}
public void study(){
System.out.println("Student:学习。专业是:major");
}
// 对父类中的eat进行了重写
public void eat(){
System.out.println("Student:多吃有营养的食物");
}
}
测试类
package com.atguigu.contact;
public class PersonTest {
public static void main(String[] args) {
Student s = new Student("计算机科学与技术") ;
// 子类调用的是子类重写的方法
s.eat() ;
System.out.println();
// 子类没有该方法,调用父类的方法
System.out.println("子类对象调用walk方法,walk方法中调用了show方法和eat方法,其中show方法没有被子类重写,eat方法有被重写:");
s.walk(10) ;
System.out.println();
// 不是重写的方法,子类自己的方法
s.study() ;
Person p = new Person() ;
// 父类对象调用肯定是调用自己的方法
p.eat() ;
}
}
输出结果:
Student:多吃有营养的食物
子类对象调用walk方法,walk方法中调用了show方法和eat方法,其中show方法没有被子类重写,eat方法有被重写:
Person走路:距离是10公里
下面为调用父类中的show方法
Person:调用show方法
下面为调用子类中的eat方法
Student:多吃有营养的食物
Student:学习。专业是:major
Person: 吃饭
3. 重写的规定:
方法的声明方式:
权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{
// 方法体
}
约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法
① 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
特殊情况:子类不能重写父类中声明为private权限的方法
③ 返回值类型:
Ⅰ 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
Ⅱ 父类被重写的方法返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
Ⅲ 父类被重写的方法的返回值类型是基本数据类型(比如double),则子类重写的方法的返回值类型必须是相同的基本数据类型
package com.atguigu.p2.ui;
public class CustomerView {
public void main(String[] args) {
a() ;
}
public int a(){
System.out.println("dfsjt");
return 1 ;
}
}
package com.atguigu.p2.ui;
public class Test extends CustomerView {
public static void main(String[] args) {
Test test = new Test() ;
test.a() ;
}
public double a(){
//这里要注意的是如果子类中有跟父类中相同方法名和形参列表的方法,那么java会自动认为你要重写该方法,如果返回值类型跟父类中的不一样,那么会报错
System.out.println("dfs");
return 1.0 ;
}
}
④ 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型(具体放到异常处理时候讲)
注意:子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)
4. 关于重写:
class Base {
int count = 10 ;
public void display() {
System.out.println(this.count) ;
}
}
class Sub extends Base {
int count = 20 ;
public void display() {
System.out.println(this.count) ;
}
}
public class FieldMethodTest {
pubilc static void main(String[] args) {
Sub s = new Sub() ;
System.out.println(s.count) ; // 20
s.display() ; // 20
Base b = s ;
//==:对于引用数据类型来讲,比较的是两个引用数据类型变量的地址值是否相同
System.out.println(b==s) ; // true
System.out.println(b.count) ; // 10
b.display() ; // 20
}
}
从结果上来看:(继承性)
从过程上看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器 ,进而调用父类的父类的构造器,直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类的结构,子列对象才可以考虑进行调用。
注意:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。
可以理解为一个事物的多种形态
对象的多态性:父类的引用指向子类的对象(或者说子类的对象赋给父类的引用)
对象的多态性只适用于方法,不适用于属性(编译和运行都看左边)
TODO问题:子类中包含自己及父类所有的方法吗?这些方法放在哪里了?是方法区吗?
有对象的多态性以后,我们在编译期,只能调用父类声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
总结: 编译看左边,运行看右边
另外, 多态是运行时行为 ,举例的话可以说equals()方法,传进去的值是用Object来接收的,如果没有多态的话,需要写很多类型的形参的equals方法。
重载不是多态,重写是多态。
练习:
第一个base.add(1,2,3) ; 调用的是Sub1中的第一个add方法,也就是这个int… arr和int[] arr算是重写,但是和int a,int b,int c不算是重写
第二个s.add(1,2,3) ; 调用的是Sub1中的第二个add方法。(因为参数是明确的)
有了对象的多态之后,内存中实际上是加载了子类特有的属性和方法,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。
那么如何才能调用子类特有的属性和方法?
要求a所属的类与类A必须是子类和父类的关系,否则编译错误
// Man继承自Person
Person p = new Man() ;
if(p instance Man){
// true
Man m = (Man)p ;
}
Person p2 = new Person() ;
if(p2 instanceof Man) {
// false 个人认为向下转型前,必须向上转型
System.out.println("true2");
}
Man m = new Man();
if(m instanceof Person) {
// true
System.out.println("true3");
}
补充:同一包下,不能命名同名的接口、类; 不同包下,能够命名同名的接口、类。
例子:
import static java.lang.Math.* ;
之后可以直接使用Math中的属性或方法:int t = sqrt(100) ;
注意: 父类 仅仅声明了有参构造函数,没有自己声明无参构造函数,则子类必须重写 父类构造函数(也就是super调用)。父类 有无参构造函数,则子类不必重写父类构造函数。
关于this和super只能选一个:无论哪个构造器创建子类对象,需要保证先初始化父类,目的是当子类继承父类后,“继承”父类中所有的属性和方法,因此子类有必要知道父类如何为对象进行初始化。
① 属性:按是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量)
实例变量: 我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。
静态变量: 我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量,是修改过了的。
② static修饰属性的其他说明:
1.静态变量随着类的加载而加载,可以通过“类.静态变量”的方式进行调用。
2.静态变量的加载要早于对象的创建;
3.由于类只会加载一次,则静态变量在内存中也会只存在一份:存在方法区的静态域中;
4.类不能调用实例变量,可以调用类变量,对象都可以调用;
③ 静态属性举例:System.out、Math.PI ;
① 随着类的加载而加载,能够通过“类.静态方法”来进行调用;
② 类不能调用非静态方法,可以调用静态方法,对象都可以调用;
③ 静态方法中只能调用静态方法或属性,非静态方法都可以(主要是由于生命周期)
在静态方法中不能使用this,super关键字,因为它不存在当前对象,它是属于类的(静态属性前面省略的不是this. 而是类.)(但是在非静态方法中,用this.调用静态属性也不会出错)
在开发中,如何确定一个属性是否要声明为static?
1.属性可以被多个对象所共享的,不会随着对象的不同而不同;
2.类中常量也常常声明为static
1.操作静态属性的方法,通常设置为static的;
2.工具类中的方法,习惯声明为static的,比如Math、Arrays、Collections
private int id ;
private static int total ; // 记录创建对象的总个数
private static int init = 1 ; // static声明的属性被所有对象共享
public Circle(){
id = init++ ;
}
final 可以声明成员变量、局部变量、方法、类
static可以修饰代码块、成员变量、方法,不可以修饰类,但是可以修饰内部类
static有一个应用是单例模式,面试常考:关于设计模式
练习两道简答面试题:
MVC是常用的设计模式之一,将整个程序分为三个层次:视图模型层、控制器层、数据库模型层。这种程序输入输出、数据处理,以及数据的战术分离开来的设计模式使程序结构变的灵活清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性。
补充一点点jvm知识:
这里可能会涉及一些面试题:比如fianl、finally、finalize的区别,==和equals的区别
补充: ==符号使用时,必须保证符号左右两边的变量类型一致。
举例:
int i = 10 ;
double j = 10.0 ;
System.out.println(i==j) ; // true
一、equals() 方法的使用:
① 是一个方法,而非运算符
② 只能适用于引用数据类型
③ Object类中的equals()方法:
public boolean equals(Object obj){
return (this==obj) ;
}
// 说明:Object中定义的equals()方法和==的作用是相同的,比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体。
④ 像String、Date、File、包装类等都重写了Object类中的equals()方法。重写之后,比较的不是两个引用
的地址是否相同,而是比较两个对象的“实体内容”是否相同。
⑤ 通常情况下,我们自定义的类如果使用equlas()的话,也通常是比较两个对象的实体内容是否相同,那么就需要对Object类中的equals()进行重写。(Eclipse中可以自动生成equals方法)
自己简单些的话,一般就是:
public boolean equals(Object obj) {
if(obj == this){
return true ;
}
if(obj instanceof User){
User u = (User)obj ;
return this.age == u.age && this.name.equals(u.name) ;
}
return false ;
}
Order order1 = new Order(1001, "AA") ;
Order order2 = new Order(1001, "AA") ;
Order order3 = new Order(1001, new String("AA")) ;
//在equals中是这样写的:
return this.orderId == order.orderId && this.orderName == order.orderName ;
system.out.println(order1==order2) ; // true
system.out.println(order1==order3) ; // false
String s1 = "BB" ;
String s2 = "BB" ;
System.out.println(s1==s2) ; // true
// 这是因为常量池
所以平时如果比较的是基本数据类型,就用==;如果是引用数据类型,就用equals
public boolean equals(Object obj) {
if(this==obj){
return true ;
}
if(obj instanceof Circle) {
Circle c = (Circle)obj ;
return this.radius==c.radius ;
}
return false ;
}
二、toString() 方法的使用
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode) ;
}
java提供了8中基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征;
基本数据类型、包装类与String类间的转换
① 基本数据类型–>包装类: 调用包装类的构造器
public class WrapperTest {
public void test1() {
int num1 = 10 ;
Integer in1 = new Integer(num1) ;
System.out.println(in1.toString()) ; // 10
Integer in2 = new Integer("123") ; // 这里的String必须是数字组合
System.out.println(in2.toString()) ; // 123
// 报异常
// Integer in3 = new Integer("123abc") ;
// System.out.println(in3.toString()) ;
Float f1 = new Float(12.3f) ;
Float f2 = new Float("12.3") ;
// Boolea有些特殊,注意下
Boolean b1 = new Boolean(true) ; // true
Boolean b2 = new Boolean("TrUe") ; // true
Boolean b3 = new Boolean("true123") ; // false
// Boolean构造器的实现,只要传进去的是true就行,不区分大小写
Oerder order = new Order() ;
System.out.println(order.isMale) ; // false
System.out.println(order.isFemale) ; // null
}
}
class Order {
boolean isMale ;
Boolean isFemale ;
}
② 包装类–>基本数据类型: 调用包装类的xxxValue()
int i1 = in1.intValue() ;
float f1 = fl1.floatValue() ;
JDK5.0性特性:自动装箱与自动拆箱
③ 基本数据类型、包装类–>String类型: 调用String重载的valueOf()方法
// 方法1:连接运算
String str1 = num1 + "" ;
// 方法2:调用String的valueOf(Xxx xxx)
float f1 = 12.3f ;
String str2 = String.valueOf(f1) ; // "12.3"
// 别的类型也一样的方法
③ String类型–>基本数据类型、包装类: 调用包装类的parseXxx(String s)
String str1 = "123" ;
// 错误情况:
// int num1 = (int)str1 ; // 不可以
// Integer in1 = (Integer)str1 ; // 两个没有继承关系的类,不能强转
int num2 = Integer.parseInt(str1) ; // 如果不是纯数字会出错,NumberFormatException
String str2 = "true" ; // 如果不是true(不分大小写,那么就是false),比如truexx
boolean b1 = Boolean.parseBoolean(str2) ;
public void test1(){
Object o1 = true ? new Integer(1) : new Double(2.0) ;
System.out.println(o1) ; // 1.0 因为三目运算符在编译的时候就要求后面两个的类型是相同的
}
public void test2 {
Object o2 ;
if(true){
o2 = new Integer(1) ;
}else{
o2 = new Double(2.0) ;
}
System.out.println(o2) ; // 1 这么写的话,对类型没有要求
}
public class JUnitTest {
int num = 10 ;
@Test
public void testEquals() {
String s1 = "123" ;
String s2 = "456" ;
System.out.println(s1.equals(s2)) ;
}
}
静态方法不能被重写,abstract修饰的方法没有方法体,类直接调没有意义。
创建抽象类的匿名子类的匿名对象:Person是一个抽象类
method(new Person(){
@Override
public void eat(){
// .....
}
});
模板方法,把基本操作组合到一起,子类一般不能重写。
模板方法中会调用重写的方法,所以不用手动去调用重写方法,直接调用模板方法就可以。
一方面,Java不支持多重继承,但是有了接口就可以得到多重继承的效果;另一方面有时必须从几个类中抽取出一些公共的行为特征,但是它们之间有没有is-a的关系,就需要用到接口。
8.接口的具体使用,体现多态性
因为接口不能实例化对象,所以在使用时只能使用它的子类,这就体现了多态性
9.接口,实际上可以看作是一种规范
10.和抽象方法一样,也能够创建匿名实现类
面试题:抽象类和接口有哪些异同?
同:不能实例化,都可以被继承;
异:抽象类有构造器,接口没有;抽象类是单继承,接口是多继承;
接口的应用
public class NetWorkTest{
public static void main(String[] args){
Server server = new Server() ;
//server.browse() ;
ProxyServer proxyserver = new ProxyServer(server) ;
proxyServer.brower() ;
}
}
interface Network{
public void browse() ;
}
// 被代理类
class Server implements Network{
@Override
public void browse(){
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() ;
work.browse() ;
}
}
两道面试题
Java8中关于接口的改进
Java中,可以为接口添加静态方法和默认方法。
静态方法:使用static关键字修饰。只能通过接口直接调用静态方法,并执行其方法体。
默认方法:默认方法使用default关键字修饰。可以通过实现类对象来调用。
比如Collections类中定义了大量的操作Collection接口的静态方法,那么之后可以慢慢的把这些方法都放在Collection中,使之具有工具类的功能。
public interface CompareA {
// 静态方法
public static void method1() {
System.out.println("CompareA:北京") ;
}
// 默认方法
public default void method2() {
System.out.println("CompareA:上海") ;
}
// 默认方法的public可以省略,但权限还是public
default void method3() {
System.out.println("CompareA:上海") ;
}
}
public class SubClassTest {
SubClass s = new SubClass() ;
// 不可以
// s.method1() ;
// 不可以
// SubClass.method1() ;
// 知识点1:接口中定义的静态方法,只能通过接口来调用
CompareA.method1() ;
// 知识点2:通过实现类的对象,可以调用接口中的默认方法
// 如果实现类重写了接口中默认的方法,调用时,仍然调用的是重写以后的方法
s.method2() ;
// 知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法
// 那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法-->类优先原则
// 知识点4:如果实现类没有继承类,但是实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错--->接口冲突
s.method3() ;
}
class SubClass extends SuperClass implements CompareA,CompareB {
pubilc void method3(){
System.out.println(SubClass:上海) ;
}
public void myMethod(){
method3() ; // 调用自己定义的重写方法
super.method3() ; // 调用父类中声明的
// 调用接口中的默认方法
CompareA.super.method3() ;
CompareB.super.method3() ;
}
}
Java基础部分就到此结束了,东西还是不少的,还有很多问题需要在今后的学习和实践中慢慢积累,此博客适合于有一定java基础,想要快速复习的同学。
努力准备秋招中,许愿大厂offer!