JAVA面向对象
-
java类及类成员
1.属性
2.方法
3.构造器
4.代码块
5.内部类 -
面向对象三大特性
1.封装
2.继承
3.多态 -
关键字
1.this
2.supper
3.static
4.final
5.abstract
6.interface
7.package
8.import -
类与对象(实例)
实例化的类即为对象
//类
class Person{
//属性
String name;
int age;
float weight;
float height;
//方法
public void eat(){
}
}
//对象(实例)
Person person1 = new Person(); //实例化类(创建对象)
Person person2 = new Person(); //实例化类
1.类中属性
- 属性与局部变量的异同点
1.直接定义在class类中的变量称为属性,定义在类的方法中的变量成为局部变量
2.属性可通过private、public、protected或缺省指定其权限
3.属性允许仅声明而使用默认的值,局部变量声明时必须初始化
class Person{
String name;//属性
int age; //属性
public void eat{
double weight = 122.2; //局部变量
}
}
2.类中方法
- 方法的定义与函数定义类似
语法如下:
权限 返回值类型 方法名(形参){方法体}
(注:java万物皆对象,无法单独定义函数,需要通过class类来封装函数(方法)
public int getAge(int age){
return age;
}
//权限修饰符 public、private、protected或缺省
- 方法的使用
1.方法可以直接调用当前类中的属性及方法
2.方法中不能再定义其它的方法
class Person{
int age;
int getAge(){
return age;//直接调用类中属性
}
void setAge(){
if(getAge() > 100) age = 100;
}
}
- 方法的重载
重载:同一个类中允许定义一个以上的同名方法,根据方法中参数的个数、参数类型的不同,在使用时动态的调用
(注:重载的方法,根据参数的数据类型、个数、顺序来区分。
说白了就是,我们调用方法时,给定一组输入参数,根据输入参数的数据类型、个数、顺序,只能有唯一的一个方法的形参满足条件)
class Person{
void print(String name){
System.out.println("Hello " + name);
}
void print(String name, int age){
System.out.printf("Hello %s, your hava %d yeas old",name,age);
}
void print(int age,String name){
System.out.printf("Hello %s, your hava %d yeas old",name,age);
}
void print(String name1 ,String name2){
System.out.println("Hello " + name1 + "," + name2);
}
}
- 方法定义中的可变个数的参数
使用语法:数据类型 … 参数名(必须放在参数列表的最后)
class class_change_parameter_num{
public static void main(String[] args){
class_change_parameter_num cls = new class_change_parameter_num();
cls.print("a");
cls.print("a","b");
}
//该方法不同于void print(String[] args)
//因为jdk5.0之前要想使用可变个数参数需要通过数组String[] args 的方式传入,jdk5.0引进简洁的String ... args 方式,编译器认为两种定义方式一致
void print(String ... args){
System.out.println("String ... args");
for(int i = 0; i < args.length; i ++){
System.out.println(args[i]);
}
}
}
- 方法参数的值传递机制
基本数据类型传递的是数值,引用数据类型传递的是地址(String除外)
(注:测试一个区分值传入的是数值还是地址可用print,如果输出结果是数值则传递的是数值,如果输出时地址,则传入的是地址)
class test{
public static void main(String[] args){
int[] arr = new int[]{1,2,3};
test f = new test();
f.f(arr);
System.out.println(arr[0]);
//此时arr[0] = 11
}
void f(int[] s){
s[0] = 11;
}
}
- 匿名对象
对象实例化时不将其赋值个某个变量,此时称为匿名对象
//正常实例化
Person p1 = new Person();
p1.getAge();
//匿名对象
new Person().getAge();
3.封装与隐藏
通常情况下,获取类中的属性值可以直接通过"类名.属性";
修改类中的属性值可以通过 “类型.属性” = xxx;
但在实际应用中,类中的某些属性在修改值时可能有一定的限制条件,要实现该功能就用到了封装的概念:
将属性声明为私有private,并自定义修改属性值的方法供用户调用(setter)
class Person{
int age;//用户可以直接访问,也可随意修改age的值,如果修改为age = -1000,则不符合常理,需限制
String name;
}
class Person{
private int age;//私有变量,只有类内部能访问,实例化后无法访问
//让用户通过该方法来修改age的值,能对修改结果限制
//此处通常称为 set方法
public void setAge(int i){
if( i > 0){
age = i;
}else {
//抛出异常
}
//属性用private修饰,用户无法直接访问,需提供访问方法
//此处通常称为get方法
public int getAge(){
return age;
}
}
- 四种修饰权限
- private
- 缺省
- protected
- public
修饰 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
---|---|---|---|---|
private | YES | |||
缺省 | YES | YES | ||
protected | YES | YES | YES | |
public | YES | YES | YES | YES |
- 四种权限的修饰范围
- 可修饰类
- 可修饰类的内部结构(属性、方法、构造器、内部类)
四种权限均可修饰类的内部结构
只有缺省或public可以修饰类
4.构造器
- 用于创建对象,未定义时,自动提供一个无参构造器;如果要在实例化时传入参数,可以自定义含参构造器
- 构造器与类同名
- 权限与类相同
class Person{
}
Person p1 = new Person();
// 此处new的Person()其实是构造器而非类,默认为 void Person(){}
- 自定义构造器
1.构造器满足重载规则
2.一旦自定义构造器,则不再提供默认的空参构造器
class Person{
//未定义构造器时,默认提供的无参构造器
public Person(){
}
public Person(String name){
}
public Person(String name, int age){
}
}
Person p1 = new Person();
Person p2 = new Person("A");
Person p3 = new Person("A",12);
构造函数不能有返回值类型,否则会被当成普通方法进行编译
- 类中构造器调用其他构造器时,必须将调用放在首行(因此一个构造器最多只能调用一个构造器)
- 所有构造器调用之间不能形成调用闭环
class Person{
public Person(){
//代码块
}
public Person(String name){
this();//保留Person()构造器的功能
//代码块
}
public Person(String name, int age){
this(name);//保留Person(String name)构造器的功能
//代码块
}
}
5.JavaBean
- javabean是应用java语言写成的可重用的组件,是指满足如下标准的java类
- 类是公共的
- 有一个无参的公共构造器
- 有属性,且有对应的get和set方法
6.this
- java中的this与python中的self使用上类似,是为了避免类中属性与方法中参数重名而设置的
- this可以理解为当前对象
- this除了可以调用属性外,还可以调用方法、构造器
- this一般情况下可以省略,除非当类中属性与方法中参数重名
class Person{
private int age;
public void setAge(int age){
age = age;//这样并没有修改类的属性age,赋值左侧的age实际上是方法的参数age
}
public void setAge1(int age){
this.age = age;//此时修改的是类的属性age
}
}
public void getAge(int age){
this.setAge1(age);//this调用方法
return this.age;
}
}
7.package与import
-
package:包,将不同功能的代码分离开,有利于管理大工程的代码
- package需要声明在java文件的首行(忽视空白行)
package test; class Person{ } class Student{ }
- package名可以用.来给包分层,每一个.代表一层目录
- 同一层目录不能有同名的类
package test.exer1; class Person{ } class Student{ }
-
import:导入指定package下的类或接口(interface)
- 置于package声明与class类之间
- java.lang下的包可以不用import,直接使用
- 本包下的类或接口,可以直接使用
- 对于不同包的同名类,如果都要使用,则需要使用包的全路径来加以区分
package test; import java.util.Arrays; // import java.util.* 导入util内所有类 class Prson{ void test(){ Arrays.toString(......); }
8.继承
- 继承:一个类复用另一个类中的属性和方法
- 关键词 extends
- 一个类可以继承给多个子类;
一个子类只能有一个父类;(单继承)
子类可以继承父类的父类的属性和方法;(多层继承)
class Person{
}
class Student extends Person{
}
//Student拥有Person中的属性和方法
//Person中权限为private的属性和方法也会继承到Student中,但是Student无法直接访问,需要在Person中封装get和set方法
- object类 如果类没有指定继承的对象,则默认继承object类
class Person{
}
//等价
class Person extends Object{
}
9.继承中方法的重写
- 自定义从父类继承得到的方法
- 重写时,方法名、参数列表必须一致;
重写后被重写的父类方法被覆盖,无法再调用;
重写时,子类的方法权限要大于等于父类的方法权限;
class Person{
public void eat(){
System.out.println("eat........");
}
}
class Student extends Person{
//重写Person中的eat方法
public void eat(){
System.out.println("eattttttttt");
}
- 子类无法重写父类的private方法
class Person{
private void eat(){
System.out.println("eat.........");
}
public void test(){
eat();
}
}
class Student extends Person{
//定义了一个eat()方法,而非覆盖父类中的eat方法,此时父类调用的仍是自身的eat方法
/*
重写与自定义的区别:
1.如果子类是直接调用自己重写或自定义的方法(如直接调用如下eat()方法),则重写或自定义得到的结果是一致的
2.如果子类调用了父类的方法,而被调用的父类的方法中又调用了自身的方法,如Person类的test方法,此时重写或自定义的区别就体现出来了
如果子类只是自定义eat方法,那么此时子类调用的test中的eat仍是父类的eat;
如果子类重写了父类的eat方法,那么此时子类调用的test中的eat其实是子类重写后的方法
具体演示可看下一个代码块
*/
public void eat(){
System.out.println("eatttttttttt");
}
}
class class_test4{
public static void main(String[] args){
Student st1 = new Student();
st1.test();
//输出 eat.........
}
}
//子类自定义跟父类同名的方法 与 重写父类方法 的区别
class Person{
public void eat(){
System.out.println("eat.........");
}
public void test(){
eat();
}
}
class Student extends Person{
public void eat(){
System.out.println("eatttttttttt");//重写父类eat()方法,此时父类调用的是此处重写的eat()方法
}
}
class class_test4{
public static void main(String[] args){
Student st1 = new Student();
st1.test();
//输出 eatttttttttt
}
}
-
1.父类被重写的方法的返回值类型是void,则子类重写时的返回值类型只能是void;
2.父类被重写的方法的返回值类型是A(引用数据类型)时,则子类重写时的返回值类型需为A或A的子类;
3.父类被重写的方法的返回值类型是基本数据类型(int,double,…),则子类重写时返回的数据类型必须一致(不满足数据类型自动提升) -
supper
调用父类中被子类重写的属性或方法(调用的是父类原始的方法,即被重写前的方法)
class Person{
int age = 13;
public void eat(){
System.out.println("eat.........");
}
public void test(){
eat();
}
}
class Student extends Person{
int age = 12;
public void eat(){
System.out.println("eatttttttttt");
}
public void test0(){
super.age; // 输出13
super.eat();//调用已经被重写的父类的方法,输入eat........
}
}
调用父类构造器
class Person{
String name;
int age;
public Person(String name){
this.name = name;
}
public Person(String name,int age){
this.name = name;
this.age = age;
}
}
class Student extends Person{
public Student(String name){
super(name);
}
public Student(String name, int age){
//调用父类构造器
super(name,age);
}
}
super调用父类构造器时必须置于构造器主体的首行,因此子类构造器只能选择调用自身类的其他构造器或调用父类构造器,不能同时调用两种构造器
在子类构造器中,如果没有显示指定调用子类或父类中的某个构造器,则默认调用super()
10.多态
将实例化后的子类用父类类型来接收
class Person{
}
class Student extends Person{
}
class test{
public static void main(String[] args){
Person p1 = new Student();
//当p1调用了父类中被子类重写的方法时,执行的子类重写后的方法
//p1无法调用Student有而Person没有的方法
}
}
多态性的作用
class Animal{
public void eat(){
System.out.println("吃饭");
}
public void shout(){
System.out.println("发声"):
}
}
class Dog extends Animal{
public void eat(){
System.out.println("狗正在吃饭");
}
public void shout(){
System.out.println("狗汪汪叫"):
}
}
class Cat extends Animal{
public void eat(){
System.out.println("猫正在吃饭");
}
public void shout(){
System.out.println("猫喵喵叫"):
}
}
class test{
public static void main(String[] args){
test p = new test();
Dog dog = new Dog();
Cat cat = new Cat();
//此处体现了多态
//声明animal,传入dog和cat实例
p.fuc(dog);
p.fuc(cat);
/*如果没有多态,则shot要按不同的animal写不同的shot方法
public void func(Dog dog){
dog.eat();
dog.shout();
}
public void func(Cat cat){
cat.eat();
cat.shout();
}
*/
}
public void fuc(Animal animal){
animal.eat();
animal.shout();
}
}
属性没有多态,多态只适用于方法
11.static
用static修饰的属性称为静态属性;用static修饰的方法称为静态方法
非static修饰的属性称为实例属性;用static修饰的方法称为实例方法
静态属性或方法属于class,而非属于对象。
静态变量或方法与实例变量或方法最直接的区别就体现在使用上,静态变量或方法无需再实例化即可调用,请看以下例子:
class Person1{
static String name = "Hello";
static void print_hello(){
System.out.println("Hello");
}
}
class Person2{
String name = "Hello";
void print_hello(){
System.out.println("Hello");
}
}
class test{
public static void main(String[] args){
//经过static修饰后的方法或属性无需实例化即可调用(体现了static修饰后的方法或属性是属于class类的,因为此处的Person1并非实例化后的,仅仅是class类)
System.out.println(Person1.name);
Person1.print_hello();
//未经过static修饰的方法或属性需实例化才可调用(体现了未经static修饰的方法或属性是属于实例(对象)的,因为此处的p2是实例化后的,而非class类)
Person2 p2 = new Person2();
System.out.println(p2.name);
p2.print_hello();
}
}
在底层实现上,静态方法或属性仅会在内存中存在一份,所有实例化后的对象共享同一份数据;而实例方法或属性,在每个实例化后的对象中单独存在一份数据,在使用上是相互独立的。(进一步体现了静态方法或属性属于class类,而实例方法或属性属于对象。因为静态方法或属性跟着class类,而class只有一份;而实例方法或属性跟着对象,实例化一个对象相当于新增一个对象,因此内存中就多一份数据)。
- 静态属性是所有对象共享的,因此当某个对象对某个静态属性值进行修改,则之后所有的实例化对象访问的到的属性值均是修改后的。
class Person{
static String name = "Hello";
static void print_hello(){
System.out.println("Hello");
}
}
class test{
public static void main(String[] args){
Person p1 = new Person();
Person p2 = new Person();
System.out.println(p2.name); //输出Hello
p1.name = "Hello World";
System.out.println(p2.name); //输出Hello World
}
}
- 静态方法中无法使用this、super,因为this、super是属于对象的。
12.代码块
class Person{
static String name;
//此为代码块,由{}包裹
{
name = "Hello";
}
}
- 代码块最多只能有static修饰,没有名称、权限、返回值类型等。
- 代码块中的内容会在class类被首次使用(static)或对象创建时(非static)自行执行,可用于对类或对象进行初始化
class Person{
static String name;
//每当创建一个新的Person对象,会在创建时自动执行以下代码块
{
System.out.println("Hello");
}
//首次使用Person类时,会自动执行以下代码块
static {
System.out.println("Hello World");
}
}
class test{
public static void main(String[] args){
Person.name = "1";//输出Hello World
Person p1 = new Person(); //输出Hello
}
}
13.关键字final
final,顾名思义,最终的,因此被final修饰过的,均是“最终版本",无法再被修改。
final修饰的结构 | 效果 |
---|---|
class类 | final class无法被继承 |
方法 | final方法无法被重写 |
变量 | final 变量为常量,无法被修改 |
形参 | final参数只能读,不能修改 |
14.abstract 抽象
-
abstract可以用于修饰class类和方法,修饰后的类或方法称为抽象类或抽象方法
-
抽象类:
- 抽象类无法被实例化,只能通过继承的子类来调用
- 抽象类虽无法被实例化,但仍有构造器,构造器在子类实例化时被调用
-
抽象方法:
- 抽象方法只有声名,没有方法体
- 包含抽象方法的类一定是一个抽象类
- 继承抽象类的非抽象子类,必须重写抽象类中所有的抽象方法
abstract class Person{
//抽象类中可以有构造器
public Person(){
}
public void eat(){
System.out.println("I can eat");
}
//抽象方法只有声明,不能有方法体
public abstract void sleep();
}
class Student extends Person{
//子类必须重写父类的抽象方法,因为抽象方法只能存在于抽象类中
public void sleep(){
System.out.println("I can sleep");
}
}
abstract class Student2 extends Person{
//抽象子类无需重写父类的抽象方法,因为抽象方法可以存在于抽象类中
}
class test{
public static void main(String[] args){
//错误,抽象类无法被实例化
Person p1 = new Person();
}
}
- 抽象类与非抽象类的区别
非抽象类是一个完整的类,可以直接实例化后进行调用,它的功能是完整的;
而抽象类仅仅是定义了一个类的基本框架,比如定义一个人,抽象类仅仅定义一个人有五官、四肢…(抽象方法),但具体的眼睛鼻子嘴巴长在哪、长成什么样、能干什么…这些都是不完整的,需要通过子类继承,完善功能后再实例化。
15.interface接口
- 接口是一种规范
- 接口和类是并列的
- JDK7及以前版本接口只能定义全局常量、抽象方法;JDK8除此之外还可定义静态方法、默认方法
interface Flyable{
//接口中定义全局常量
public static final int MAX_SPEED = 12345;
int MIN_SPEED = 0;//虽然此处没有用final修饰,但仍是个全局常量,也就是说接口中的变量均是常量,而修饰常量的public static final可以省略
//接口中定义抽象方法
public abstract void fly();
void stop();//可以省略abstract
}
- 接口无法定义构造器,因此无法实例化
- 接口由class类实现(implements)的方法来使用,且类必须实现接口中的所有抽象方法
class Plane implements Flyable{
@override
public void fly(){
...
}
@override
public void stop(){
...
}
}
- class类可以同时实现多个接口
class Plane implements Flyable,interface2,...{
@override
...
}
- class类可以同时继承类和实现接口
class class2 extends class1 implements interface,interface2,...{
@override
...
}
- 接口可以继承其他接口,且是多继承的
interface interface1 extends interface2,interface3,...{
}
- 类多实现接口时,如果多个类同时定义了同一个全局变量,则类中必须重新声明,否则无法使用
interface inface1{
public static final int MAX = 123;
}
interface inface2{
public static final int MAX = 12;
}
class class0 implements inface1,inface2{
//如果没有重新声明MAX,则实例化class0后,无法使用,会错先错误:
//both variable MAX in inface1 and variable MAX in inface2 match
public static final int MAX = inface1.MAX;
}
- 抽象类与接口的异同
…
16.内部类(类的嵌套)
- 如果将类A放在类B内中声明,则A称为B的内部类,B成为外部类
- 内部类分为:
- 成员内部类(定义在方法外)
- 局部内部类(定义在方法内、构造器内、代码块内)
class A{
class E{};//成员内部类
static class F{};//静态成员内部类
public void test(){
//局部内部类
class B;
}
{
//局部内部类
class C;
}
public A(){
//局部内部类
class D;
}
}