类
成员变量和局部变量
-
成员变量:定义在类中
-
局部变量:定义在方法中
public class Car{
int a;
public void show(){
//int b;
System.out.println(a);
//System.out.println(b); 没有赋值,报错
}
public static void main(){
Car c = new Car();
c.show();
}
}
在没有赋值的情况下,局部变量会报错,但是成员变量Java会给为其添加一个默认值。
构造方法
通过new创建对象的过程其实是通过该类的构造方法创建创建对象,如果没有手动定义构造方法,系统会自动添加一个无参的构造方法。但是如果一旦手动定义了有参的的构造方法,就不能通过无参的构造方法去创建对象。
public class Phone{
// 如果没有定义,默认会添加一个没有任何操作的无参构造
public Phone(){
System.out.println("我是无参构造");
}
// 构造方法名与类名一致,有参
public Phone(int age){
System.out.println(age);
}
}
public class Telphone{
public static void main(String[] args){
Phone phone = new Phone(); // 通过无参构造方法创建对象
Phone phone = new Phone(20); // 有参构造创建对象
}
}
构造方法和普通方法一样,都有重载的概念。
静态static
我们知道一个类可以创建多个对象,每个对象都有自己的成员,如果要使得一个类和它的所有对象共享同一个成员(变量、方法),那么就需要static了。
public class Person{
static int age = 20;
public static void main(String[] args){
// 可以通过类名直接访问
System.out.println(Person.age); //20
// 也可以通过对象名访问
Person person = new Person();
System.out.println(person.age); //20
// 通过对象修改
person.age = 30;
System.out.println(Person.age); // 30
}
}
可以看出,通过类名和对象名调用的都是同一个age。
静态方法和变量是一样,这里不敲代码了。但是需要注意几点:
1、在静态方法中,不能直接调用非静态的方法和变量。如果想要调用可以通过创建对象的方法去调用非静态成员。
2、在普通成员方法中,可以直接访问同类的非静态方法和静态方法。
public class Person{
String name = "神雕侠侣";
static int age = 100;
//普通方法
public void show(){
System.out.println("北京欢迎你");
}
// 静态方法
publiic static void print(){
// 静态方法中不能访问非静态
// System.out.println(name);
System.out.println(age); //可以访问静态变量
// 不能直接访问非静态方法,可以通过对象访问
//show();
Person person = new Person();
person.show();
}
}
初始化块
Java中可以通过初始化块进行数据赋值。这里结合static介绍一下普通初始化块和静态初始化块。
public class Hello{
int num1;
int num2;
static int num3;
public Hello(){
num1= 10;
System.out.println("构造函数中给num1赋值");
}
{
num2 = 20;
System.out.println("初始化块中给num2赋值");
}
static {
num3 = 30;
System.out.println("静态初始化块中给num3赋值");
}
public static void main(String[] args){
Hello hello = new Hello();
// 静态方法中调用非静态成员,通过对象调用
System.out.println("我是num1:"+hello.num1);
System.out.println("我是num3:"+num3);
Hello hello2 = new Hello();
}
}
/*
静态初始化块中给num3赋值
初始化块中给num2赋值
构造函数中给num1赋值
我是num1:10
我是num3:30
初始化块中给num2赋值
构造函数中给num1赋值
通过打印结果可以看出:静态初始化块最先执行,然后执行普通初始化块,最后才执行构造方法。但是静态初始化块只在类加载的时候执行一次,所以当第二次创建对象hello2的时候并没有执行静态初始化块。
内部类
内部类就是定义在一个类之中的类,而这个被定义了内部类的类就是外部类。内部类有四种:
-
成员内部类(普通内部类)
-
静态内部类
-
方法内部类
-
匿名内部类
1、成员内部类
-
成员内部类可以使用任何访问修饰符修饰
-
成员内部类中可以访问任何外部类的成员(不受访问修饰符限制),外部类不能直接访问内部类成员,只能通过创建内部类对象访问。
-
如果外部类和内部类定义了相同成员名,在内部类中默认访问内部自己的成员,要访问外部成员可以使用
外部类名.this.成员名
-
在内部类的静态方法中不能直接通过new去创建一个内部类对象,而是通过外部类对象.new去创建。
如果是普通方法中可以直接创建内部类
。 -
定义了内部类的源文件,变异后会生成两个class文件。
外部类.class
和外部类$内部类.class
。
//外部类HelloWorld
public class HelloWorld{
//外部类的私有属性name
private String name = "imooc";
//外部类的成员属性
int age = 20;
//成员内部类Inner
public class Inner {
String name = "xiaoxiao";
//内部类中的方法
public void show() {
// 访问外部与内部同名私有变量,用外部类.this.变量
System.out.println("外部类中的name:" + HelloWorld.this.name);
//默认访问的是内部类的变量
System.out.println("内部类中的name:" + name);
// 直接访问外部类成员
System.out.println("外部类中的age:" + age);
}
}
// 静态方法中不能直接调用内部类方法
public static void main(String[] args) {
//先创建外部类的对象
HelloWorld o = new HelloWorld ();
//通过外部类对象.new创建内部类的对象
Inner inn = o.new Inner();
//调用内部类对象的show方法
inn.show();
}
// 如果是普通方法可以直接创建内部类对象
/*public void console(){
Inner inn = new Inner();
inn.show();
}*/
}
2、静态内部类
其实静态内部类就是给普通内部类添加了static修饰符。但是在使用上还是有多大区别的。
-
静态内部类不能直接访问外部类的非静态成员,可以通过new创建外部类对象的方式访问。
-
如果外部类的静态成员和内部类成员名相同,可以通过
外部类名.成员名访问
该成员,如果成员名不相同,可以直接访问外部类的静态成员。 -
创建静态内部类对象的时候也不需要向普通内部类那样先创建外部类了,可以直接通过new创建内部静态类对象。
public class HelloWorld {
// 外部类中的静态变量score
private static int score = 84;
private static int age = 100;
// 创建静态内部类
public static class SInner {
// 内部类中的变量score
int score = 91;
public void show() {
System.out.println("访问外部类中的静态变量score:" + HelloWorld.score); //外部类名.外部静态成员名
System.out.println("访问内部类中的score:" + score);
System.out.println("访问外部类中的静态变量age:" + age); //直接外部静态成员名
}
}
public static void main(String[] args) {
// 直接创建内部类的对象
SInner si = new SInner();
// 调用show方法
si.show();
}
}
3、方法内部类
方法内部类的位置略有不同,它是在外部类的某一个方法中。并且只在该方法内可用。
public class HelloWorld {
// 外部类中的show方法
public void show() {
// 定义方法内部类,不能使用访问控制符合static修饰符
class MInner {
int score = 83;
public int getScore() {
return score;
}
}
// 创建方法内部类的对象
MInner mi = new MInner();
// 调用内部类的方法
int score = mi.getScore();
System.out.println("成绩:" + score );
}
}
由于方法内部类只能在方法内使用,所以方法内部类不允许使用访问控制符合static修饰符。
4、匿名内部类
匿名内部类存在的前提是存在一个类(抽象类、具体类都可)或者接口,主要针对不能直接创建对象的抽象类或者接口。应用场景不止一种,刚刚接触理解可能不到位,就只写个简单例子。
public class Main{
public static void main(){
Outer o = new Outer();
o.show(); //我是一个内部类,实现Inter接口
o.show2(); //我是一个匿名内部类,实现Inter接口
}
}
//创建一个接口
interface Inter{
public void print();
}
// 创建外部类
class Outer{
// 通过创建内部类实现接口,重写方法
class Inner implements Inter{
public void print(){
System.out.println("我是一个内部类,实现Inter接口");
}
}
public void show(){
Inner inner = new Inner(); //普通方法中(非静态),可以直接创建内部类对象
inner.print();
}
// 通过匿名内部类创建,匿名内部类要在方法中
public void show2(){
new Inter(){
public void print(){
System.out.println("我是一个匿名内部类,实现Inter接口");
}
}.print();
}
}
通过以上代码可以看到,通过匿名内部类实现接口的代码更简洁,但是理解起来会稍微吃力一些。
继承
final关键字
final是“最终”的意思,顾名思义,被final修饰的类不能被继承;修饰的方法不能被重写;属性不能被默认初始化赋值,只能在定义时赋值或者在构造方法中赋值;变量不能被二次赋值,只能在声明的时候赋值,即常量。
super的使用
如果子类重写了父类的方法或者属性,通过super
可以在子类中调用父类的方法和属性。
public void show(){
// 直接打印子类自己的属性age
System.out.println(age);
// 通过super调用父类的age属性
System.out.println(super.age);
}
// 调用方法亦是如此
super与构造方法也有很大的联系,在一个子类的构造方法中,必须要通过super()调用父类的构造方法,如果要写一定要写在构造方法的第一行,如果没写系统会隐式添加super(),默认调用父类无参构造。
如果在父类中自己手动添加了有参的构造方法,系统就不会默认生成一无参的构造方法,此时如果在子类不手动调用super(),隐式调用无参构造时就会报错。
Object类
和js一样Object类是所有类的父类,如果一个类没有继承任何类那么它就默认继承Object类。可以使用Object类的所有方法,比如toString()
、equals()
。在第一篇基础文章中已经简单介绍过equals()了。
多态
所谓多态,就是同一个行为有多种不同的表现形态。在面向对象中,多态的前提一定是继承关系。在创建对象时可以用父类指向父类对象,也可以用父类指向子类对象。
public class Man extends Person{
public static void main(){
Person p = new Person();
Person p2 = new Man();
}
}
以上仅仅是举个例子用来理解多态的引用,当然如果父类指向父类对象,那使用方法时就使用父类的方法,没有问题;如果父类指向子类对象,那么使用方法时就指向子类的方法。如果子类没有重写该方法就指向子类继承下来的方法。
引用类型转换
之前在基础介绍过基础类型转换,其实引用类型转换也是同样的,也分为自动类型转换(子类转向父类)和强制类型转换(父类转向子类)。
还是继续父类(Person)和子类(Man)的例子:
Man man = new Man (); //创建一个子类对象
Person person = man; // 赋值给一个父类类型的变量,会进行自动类型转换
如果又要把person变回Man类型呢?这时是父类向子类转(大转小),需要进行强制类型转换。
Man man2 = (Man) person;
但是问题来了,以上代码暂时没有什么问题,如果我要把person转成另外一个子类Woman呢?
Woman woman = (Woman) person;
这样是会报错的,要想避免这种情况出现,我们需要使用到instanceof 进行判断。某个对象是否属于某个类型
if(person instanceof Woman){
Woman woman = (Woman) person;
}else{
System.out.println("该对象不属于Woman类型,不能进行强制类型转换");
}
抽象类
抽象类的作用其实是来约束子类必须实现哪些方法,而不会关心具体是怎么实现的。
abstract class Person{
public abstract void show();
}
以上代码是最简单的抽象类的写法,通过abstract关键字来生命抽象类或者抽象方法。抽象方法没有方法体,直接以;
结束。
定义了抽象方法的类一定是抽象类。
所以,所有要继承Person的子类,一定要实现show方法,但是这个方法内你要实现什么无所谓。
接口
这里额外说明一点:可能抽象类和接口放在多态的标题下不合适。由于是边学习边总结,所以笔记是跟着教学走的。
-
接口没有构造方法,不能被实例化
-
一个接口可以继承多个接口(类只能单继承)
-
一个类可以继承一个父类,实现多个接口,接口直接用
,
分隔,先写继承再写实现。 -
接口中的变量一定都是常量(不要纠结有语病),方法一定都是抽象方法。
public (abstract) interface Inter{
public static final String name = "人类";
public (abstract) void show();
}
public class Man extents Person implements Inter{
public void show(){
System.out.println("必须实现接口的抽象方法");
}
}
接口中的变量和方法只能使用以上修饰符修饰,括号中的abstract表示可以省略不写,系统会自动添加。
结合之前学习的匿名内部类,我们来通过匿名内部类来实现一个接口
Inter inter = new Inter(){
public void show(){
System.out.println("匿名内部类实现接口");
}
};
inter.show();
网友评论