前言
1. 接口
Java前面的很多基础知识,我都没有整理成博客,如果需要的话,可以去看我的Gitee,https://gitee.com/once-three-hearted-grass/java
1.1 认识接口
实现接口要用关键字interface
public interface A {
}
这就是一个接口,接口里面只能写成员变量和方法,而且成员变量就是常量
看这个可以看出public static final是灰色的,说明不写也是默认的public static final
public interface A {
int a=0;
}
System.out.println(A.a);
接口里面的方法也是抽象方法
所以接口不能定义出对象
而且接口里面只能有成员变量和方法,不能有代码块,构造方法
接口是用来被类来实现的,一个类可以继承多个接口,然后类还必须实现完接口的全部抽象方法
public interface C {
}
public class B implements A,C {
@Override
public void test() {
}
}
1.2 接口新增三种方法
默认方法,这个方法必须要用default修饰,而且默认是public的,接口因为要被继承,所以方法都是默认public的
public interface A {
int a=0;
public abstract void test();
default void test1(){
System.out.println("aaaaa");
}
}
这个test1的方法就相当于是一个普通方法,能够被对象调用使用
由于接口无法创建对象,所以只能由继承它的子类去调用
B b=new B();
b.test1();
第二个就是私有方法,这个方法必须使用private修饰,不然的话,就会默认使用public修饰,就是普通的抽象方法了
default void test1(){
System.out.println("aaaaa");
test2();
}
private void test2(){
System.out.println("bbbbb");
}
由于这个是私有的,也不能被子类继承,所以就只能在自己类里面使用
只能在默认方法里面使用,才能发挥价值了
第三个就是静态方法
用static修饰的方法
static void test3(){
System.out.println("ccccc");
}
A.test3();
既然是静态方法,那么就可以用类名进行访问了
1.3 多继承
类继承接口用的implements
接口继承接口用的就是extends
public interface D extends A,C{
}
而且是可以多继承的,这样的好处就是,多个接口合在一起了,这样用子类继承的时候,就只用写D这一个了
注意事项
public interface A {
int a=0;
public abstract void test();
default void test1(){
System.out.println("aaaaa");
test2();
}
private void test2(){
System.out.println("bbbbb");
}
static void test3() {
System.out.println("ccccc");
}
}
public interface C {
int test();
}
接口继承多个接口时,如果返回值不一样,不能被多继承,因为不知道怎么实现了,也不能被多实现,,参数不一样的话,还可以继承
一个类继承了父类,也继承了接口,父类和接口有同名的默认方法,那么会优先使用父类的默认方法
一个类实现了多个接口,如果多个接口有同名的默认方法,会冲突
public interface C {
default void test1(){
System.out.println("aaaaa");
}
}
但如果想不冲突的话,可以去类里面重写这个方法
**public class B implements D {
@Override
public void test() {
}
public void test1() {
}
public void test(int a) {
}
}**
类里面重写了这个test1方法,那么下次调用就会调用这个方法
2. 内部类
如果一个类定义在另一个类的里面,那么这里面的这个类就是内部类
2.1 成员内部类
public class Outer {
public class Inner{
int a;
public void test1(){
System.out.println("aaaaaaaa");
}
}
}
public class这个就是成员内部类,和普通的类一样,成员内部类里面想定义什么就定义什么
Outer.Inner in=new Outer().new Inner();
创建外部类就不用说了,创建内部类就是这样的,要先new出外部类,然后才可以new出内部类
public class Outer {
int a=10;
public class Inner{
int a=20;
public void test1(){
int a=30;
System.out.println("aaaaaaaa");
}
}
}
看这个,如何打印出三个a的值呢
public class Outer {
int a=10;
public class Inner{
int a=20;
public void test1(){
int a=30;
System.out.println("aaaaaaaa");
System.out.println(a);
System.out.println(this.a);
System.out.println(Outer.this.a);
}
}
}
这样就可以了,当然还是就近原则,如果局部没有a,就不用this了
就这样我们就可以访问局部a,内部类的a,和外部类的a了
2.2 静态内部类
public class Outer {
int a=10;
public static class Inner{
int a=20;
public void test1(){
}
}
}
虽然静态内部类是里面的类,但是使用起来,就是相当于一个独立出来的类
所以创建对象的时候就不用先创建外部类,在创建内部类了
Outer.Inner in=new Outer.Inner();
创建内部类的对象的时候就这样就可以了
然后就是内部类只能访问外部类的静态成员,非静态成员是无法访问的,
2.3 局部内部类
这个内部类就是定义在外部类的成员方法里面的类,出了作用域就被销毁了,没什么用
2.4 匿名内部类
匿名内部类就是相当于匿名对象那样,这个你不用创建类,直接就创建好了对象
abstract class Outer {
int a=10;
static int b=20;
abstract void test();
}
new Outer(){
@Override
void test(){
}
};
这就是一个匿名内部类,这个代码的作用就相当于就是,先用个子类继承,重写抽象方法,然后在new出来,所以可以用一个父类去接收
public class test {
public static void main(String[] args) {
Outer in=new Outer(){
@Override
void test(){
System.out.println("aaaaa");
}
};
in.test();
}
}
这样写就不用再创建一个子类去接收了
2.5 实例
接下来我们讲的这个实例是一个GUL编程,不太懂也没事
public class test {
public static void main(String[] args) {
JFrame win=new JFrame("登录界面");//创建一个登录界面
JButton btn=new JButton("登录");//创建一个登录按钮
win.add(btn);
win.setSize(400,400);//设置窗口大小
win.setLocationRelativeTo(null);//窗口居中
win.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//设置一个关闭按钮
win.setVisible(true);//可以看见窗口
}
}
但是点了登录也没有反应,而且还可以点除了登录以外的其他部分,所以我们还要修改一下
要给登录按钮一个事件
public class test {
public static void main(String[] args) {
JFrame win=new JFrame("登录界面");//创建一个登录界面
JPanel panel=new JPanel();//创建一个面板,防止点到了其他部分,有了面板,那么就点不到其他部分了
win.add(panel);//覆盖面板上去
JButton btn=new JButton("登录");//创建一个登录按钮
panel.add(btn);//面板上面增加按钮
//btn.addActionListener();//括号里面增加的就是事件
win.setSize(400,400);//设置窗口大小
win.setLocationRelativeTo(null);//窗口居中
win.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//设置一个关闭按钮
win.setVisible(true);//可以看见窗口
}
}
如何给按键增加事件呢,我们可以查看这个方法的定义,左手ctrl,右手点击就可以查看方法定义了
然后ActionListener是一个接口
所以我们可以创建一个这个接口的子类对象,反正父类可以接受子类的
创建这个接口的子类对象那么就用匿名对象了
btn.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
//
}
});
然后actionPerformed就是要表演的事件了
public class test {
public static void main(String[] args) {
JFrame win=new JFrame("登录界面");//创建一个登录界面
JPanel panel=new JPanel();//创建一个面板,防止点到了其他部分,有了面板,那么就点不到其他部分了
win.add(panel);//覆盖面板上去
JButton btn=new JButton("登录");//创建一个登录按钮
panel.add(btn);//面板上面增加按钮
btn.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(win,"登录一下");//这个就是在桌面上打印登录一下
}
});
win.setSize(400,400);//设置窗口大小
win.setLocationRelativeTo(null);//窗口居中
win.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//设置一个关闭按钮
win.setVisible(true);//可以看见窗口
}
}
或者后面我们可以使用lamda表达式,这个创建对象比匿名还简单
3. 枚举
3.1 基本概念
public enum A {
X,Y,Z;
}
枚举的第一行只能写一些变量名,变量与变量之间用逗号隔开,这些都是枚举类型的变量,而且都是常量
然后后面的内容就和普通的类是一样的了
public enum A {
X,Y,Z;
int a=10;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
}
但是如何创建变量呢
A a1=A.X;
System.out.println(a1);
创建变量就这样创建变量,直接用.操作符就可以了
然后因为说了的,枚举的类型都是常量,所以打印出来的也都是常量,就直接打印出变量名了
因为枚举类型只有那么几个变量,所以它的构造器是私有的,是在第一行的时候就调用的
第一枚举类不能创建对象
第二就是枚举类都是最终类,不能被继承,是用final修饰的
第三就是枚举类提供了一些额外的API,就是方法
A[] arr=A.values();//这个values会拿到A的全部对象,然后返回在一个数组中
A a3=A.valueOf("Z");//这个就是指定拿到常量Z
System.out.println(a3.name());//这个就是返回a3的名字,就是Z
System.out.println(a3.ordinal());//这个就是返回a3变量的下标,,X下表为0,和数组一样
3.2 抽象枚举
接下来我们来讲一下抽象枚举,何谓抽象枚举呢,这个的意思就是,枚举中有抽象方法,然后每个变量都要重载这个抽象方法的意思
public enum A {
X(){
@Override
public void go() {
}
},Y(10){
@Override
public void go() {
;
}
};
int a=10;
public abstract void go();
A(int a) {
this.a = a;
}
A(){}
}
我们看这个就可以了,在第一排重写方法的时候,()里面的内容就对应了构造方法,然后就是一定要重写抽象方法,不然根本无法使用对应的抽象方法
在讲一下枚举作用下的单例模式
public enum B {
X;
}
就这样就可以l了
3.3 应用
枚举一个比较重要的作用就是它的对象也就是常量,可以作为swich语句
public enum B {
X,Y,Z;
}
B b=B.X;
switch (b){
case X:
System.out.println("这个是X");
break;
case Y:
System.out.println("这个是Y");
break;
case Z:
System.out.println("这个是Z");
break;
}
4. 泛型
这里的泛型是和<>结合的,与C++的模板实例化很相似
4.1 认识一下
看这个普通的ArrayList是可以接受Object类型的,就是可以接收任意类型的
看这个指定了String,那么就只能填入String了
ArrayList<String> arr=new ArrayList<>();
arr.add("aa");
然后就是后面的那个String可以省略掉
像这样<>里面可以放类型的就是泛型
4.2 简单模拟StringList
还不能这样写,因为Java中是不能创建泛型类型的数组的,只能写Object了,为什么写这个呢,因为我们传入的E(只能引用类型),肯定 直接或间接继承了Object的,第一可以创建数组,对象,第二就算创建了也不影响,因为子类可以赋值给父类,这也是E只能为引用类型的原因,因为普通类型无法继承
public class MyStringList<E> {
private Object[] arr=new Object[10];
private int size;
public boolean add(E e){
arr[size]=e;
return true;
}
}
如果没有指明E的类型,那么就默认是Object的了,就可以插入任意类型了
MyStringList<String> ar=new MyStringList<>();
ar.add("aaaa");
这个函数这样写还不行,因为类型不匹配
public E get(int index){
return (E)arr[index];
}
这样写就可以了
4.3 增加知识
除了有一个泛型变量外,还可以有多个
public class MyClass<E,T> {
}
MyClass<String,String> m=new MyClass<>();
这里就需要写两个泛型了,要么就一个都不写
public class MyClass<E extends A> {
}
public class B extends A{
}
这里就表示传入的泛型必须是A的子类或者就是A本身
MyClass<A> m=new MyClass<>();
MyClass<B> m1=new MyClass<>();
4.4 泛型接口
这里的泛型接口的意思就是接口也设置了泛型
public interface Data<E> {
public boolean add(E e);
public E get(int index);
}
这就是一个泛型接口,因为接口无法创建出对象,所以接口就无法实例化,但是接口必须被继承,所以就要在继承那里实例化了
public class AData implements Data<A>{
@Override
public A get(int index) {
return null;
}
@Override
public boolean add(A a) {
return false;
}
}
看这个,AData继承了Data,那么顺便就把泛型传过去了
4.5 泛型方法
泛型方法就是在方法那里加上泛型,具体表现为
public static <T> void print(T t){
System.out.println(t);
}
这个就是泛型方法,<>放在返回值的前面
print("aaaaa");
print(1);
它的使用就是这样使用的,不用特地说明T是什么类型,直接在传参的时候就确定了T是什么类型,,<>里面不能放普通类型,但是传参的时候就可以放普通类型,为什么呢,待会说一下
public boolean add(E e);
上面这个就不是泛型方法,因为泛型方法是在传参的时候才确定泛型,而这个是在创建类变量的时候就确定了,这个E就是相当于一个类型了,不是泛型
public static <T> void go(ArrayList<T> a){
}
ArrayList<String> arr=new ArrayList<>();
arr.add("aa");
arr.add("bb");
arr.add("cc");
go(arr);
这里写ArrayList的目的就是指明只能传ArrayList才能确定这个泛型,不然随便传个引用类型变量就确定了
public static <T extends A> void go(ArrayList<T> a){
}
这个就表示ArrayList里面的T必须还是A的子类,或者A本身
ArrayList<B> arr=new ArrayList<>();
B b1=new B();
B b2=new B();
arr.add(b1);
arr.add(b2);
go(arr);
这个
public static <T> void go(ArrayList<T> a){
}
就等价于
public static void go(ArrayList<?> a){
}
这个问号就表示接受的是ArrayList<?>类型的,里面的?表示任意类型
这个
public static <T extends A> void go(ArrayList<T> a){
}
也等价于
public static void go(ArrayList<? extends A> a){
}
表示任意类型,这个类型还是A的子类或者A
这个?就叫做通配符,,,,extends A叫做它的上限,表示最上可以是A,要么就是A的子类
如果是super A的话,这个就表示是它的下限,表示,最差是A,要么就是A的父类
泛型不支持基本数据类型,因为int并没有继承Object,所以是不行的,,但是如果我们就是要传入int数据呢,可以这样
ArrayList<Integer> qq=new ArrayList<>();
qq.add(1);
qq.add(1);
qq.add(1);
Integer和int是一个意思,只不过它是一个类,继承了Object,所以可以被使用
ArrayList<Double> qq=new ArrayList<>();
qq.add(1.1);
qq.add(1.3);
qq.add(1.3);
至于double就用Double
这也是为什么泛型方法时可以传入int数据了,因为被当做了Integer,所以就可以用了
总结
这一节讲的主要是Java的一些基本知识,下一次讲的就应该是Java的主要API了