在为数不多的学习生涯中,我也接触过面向对象的编程,也较为粗糙的学习过java的面向对象编程,但是我承认,当时自己没有好好学,所以再以此次机会重新温习下java的面向对象编程,内容肯定不全面,而且,有些东西我也解释不清。但我尽力。
在开始之前,我接下来的所写的很大一部分的了解是来自于我现在学的尚学堂的免费课程。虽未刚了解到了尚学堂,感觉其公司还是很人道的。
面向对象与面向过程的区别
我刚开始是接触的c语言,c语言是一个面向过程的语言,面向过程就肯定强调的是对于过程的重要性,就比如说,如何把大象塞进冰箱?1。打开冰箱,2。把大象放进去,3。关上冰箱 ,但是对于生产一辆汽车呢?还可以以面向过程的去完成他吗?首先呢,是肯定可以完成的,但是肯定更耗时耗力。因为车的制造是很麻烦的,所以我们就应该使用面向对象,不能在按步骤去实现。
面向对象和面向过程的一个总结
- 都是解决问题是一个思维方式,都是代码组织的方式
- 解决简单问题可以使用面向过程
- 解决复杂的问题:宏观上用面向对象把握,微观处理上还是面向过程。
类和对象
对象是对客观事物的抽象,类是对对象的抽象。类是一种抽象的数据类型。它们的关系是,对象是类的实例,类是对象的模板。对象是通过new className产生的,用来调用类的方法;类的构造方法 。
在我看来,什么是一个类?就是很生活化的一个名词,比如说“人-类” 何为人? 有头,有四肢,可以思考,要吃饭。我们就可以把拥有这样特征或者术语(属性)的划为一个类
下面是一个简单的类与对象的例子
public class t1 { //t1 是一个学生类 t1= Student
//属性(fields)
String name;
int age;
String school;
Computer com;
//方法
void play(){
System.out.println(name+"正用"+com.brand+"在玩英雄联盟");
}
void study(){
System.out.println(name+"正在用"+com.price+"的"+com.brand+"电脑学习java编程");
}
//构造方法
public t1(){
}
public static void main(String[] args) {
t1 stu = new t1();
stu.name="田坤";
stu.age=18;
stu.school="西安理工大学高等技术学院";
Computer com = new Computer();
com.price=4200;
com.brand="DELL";
stu.com=com;
System.out.println(stu); //stu对象的地址
System.out.println(com); //com对象的地址
stu.study();
stu.play();
}
}
class Computer{ //电脑类
String brand;
int price;
}
OPP_1.t1@16d3586
OPP_1.Computer@154617c
田坤正在用4200的DELL电脑学习java编程
田坤正用DELL在玩英雄联盟
内存分析的详情_栈_堆_方法区_栈帧_程序执行的变化过程
JAVA的虚拟机可以分为三个部分:栈stack 堆 heap 方法区 method area
栈的特点如下:
- 栈描述的是方法执行的内存模型,每个方法被调用都会创建一个栈帧(存储局部变量,操作数,方法出口等)
- JVM(java虚拟机)为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数,局部变量等)
- 栈属于线程私用,不能实现线程间的共享
- 栈的存储特性是“先进后出”
- 栈是由系统自动分配,速度快,栈是一个连续的内存空间。
堆的特点如下:
- 堆用于存储创建好的对象和数组(数组也是对象)
- JVM只有一个堆,被所有线程共享
- 堆是一个不连续的内存空间,分配灵活,速度慢!
方法区(静态区)特点如下:
- JVM只有一个方法区,被所有的线程共享
- 方法区实际上也是堆,只是用于存储类,常量相关的信息
- 用来存放程序中永远是不变或者唯一的内容(类信息 {class对象} 静态变量 字符串常量等)
---------图像画的有点的有点丑
构造方法(对对象进行初始化)
要点:
- 通过new关键字调用
- 构造器虽然有返回值,但不能定义返回值类型(返回值类型肯定是本类)不能在构造器中返回某个值
- 如果我们没有写构造方法,那么编译器会帮我们默认生成一个没有参数的构造方法,如果我们写了,编译器便不会帮我们生成
- 构造方法名必须和类的名字相同
public class t2 { int x; int y; //编译器会自动帮我们生成一个无参的构造方法 } class t3{ int x; int y; //这是我们自己写的构造方法 public t3(int _x,int _y){ x=_x; y=_y; } } class t2_Test{ public static void main(String[] args) { t2 tt = new t2(); t3 ttt = new t3(3,4); } }
如果我们在自己定义构造方法,编译器便不会帮我们写出无参的构造方法,我们在写的时候最好加上;
构造方法的重载
我理解就是我们可以以多种途径去初始化对象,是我们的程序使用起来更加便捷
public Student{
String name;
int age;
String school;
//无参构造方法
public Student(){
}
public Student(String name){
super(); //构造方法的第一句总是super()
this.name = name;
}
public Student(String name,int age,String school){
this.name = name;
this.age = age;
this.school = school;
}
}
class TestStudent{
public static void main(String aaa[]){
Student stu1 = new Student();
Student stu2 = new Student("田坤");
Student stu3 = new Student("田坤",18,"西安理工大学高等技术学院");
}
}
java中的垃圾回收机制暂时放着一个空缺
this关键字
this表示的就是当前的对象
俩个用法:1.避免程序二义性的的发生 2.可以用this来调用重载的构造方法,并且必须是第一句
public class Student {
String name;
int age;
String school;
Student(){
}
Student(String name,int age){
this.name = name ;
this.age = age;
}
Student(String name,int age,String school){
this(name,age); //使用this来调用重载的构造方法必须是第一句
this.school = school;
}
}
Static关键字
在类中,用static声明的成员变量为静态成员变量,也称为类变量,类变量的生命周期和类相同。(类变量从属于类),类的整个执行期间都有效
static修饰的成员变量和方法从属于类,普通的成员变量和方法从属于对象
Static特点:
- 在该类中被共享
- 调用===>类名。
- 在static方法中不可以直接访问非static的成员
public class Student1 {
String name;
int age;
static String school="西安理工大学高等技术学院";
Student1(String name,int age){
this.name = name;
this.age = age;
}
//非静态方法
public void play(){
//非静态方法中可以调用静态方法
System.out.println(school);
printSchool();
System.out.println("玩英雄联盟");
}
//静态方法
static void printSchool(){
//play(); 静态方法中不可调用静态方法
System.out.println(“我所在的学校是”+school);
}
public static void main(String[] args) {
Student1 stu = new Student1("田坤",18);
stu.play();
//调用静态方法
Student1.printSchool();
}
}
静态方法中为什么不能调用非静态方法,原因就是静态方法存在的时间比非静态存在的时间久;就如上面标红的字所说的那样
可以看下图更好理解
静态初始化块
构造方法是对对象的初始化,而静态初始化块用于类的的初始化操作,在静态初始化块中不能直接访问非static 成员
静态初始化块的执行顺序是先执行祖先的静态初始化块,那也就因为这必先执行object的里面的静态初始化块,构造方法的顺序也是次顺序
public class Student2 {
String name;
int age;
static String school;
static { //在类初始化时执行
school = "西安理工大学高等技术学院";
}
public void printSchool(){
System.out.println(school);
}
public static void main(String[] args) {
Student2 stu = new Student2();
stu.printSchool();
}
}
参数的传值机制
Java中的所有的参数传值都是“值传递”,也就是传递的是值的副本,我们得到是副本,因此副本的改变不会影响原件
- 基本数据类型参数的传值:传递的是副本。
- 引用数据类型:传递的是“对象的地址”,副本和原件都指向一个地址。如见的改变会影响原件
public class Student2 {
String name;
int age;
static String school;
Student2(String name,int age){
this.name = name;
this.age = age;
}
public void changeName(Student2 stu){
stu.name="田坤2号";
}
public static void main(String[] args) {
Student2 stu = new Student2("田坤",18);
System.out.println(stu.name);
//改变副件
stu.changeName(stu);
System.out.println("副件改变后:"+stu.name);
}
}
田坤
副件改变后:田坤2号
包(package)
类似于文件夹,用于管理。
用法:
- 类的第一句非注释语句
- 包名:域名倒着写即可,再加上模块名,并与内部管理类
写项目的时候,我们尽量不要使用默认包,还有就是com.gao 和 com.gao.gar 这俩个包没有包含关系,是俩个完全独立的包,只是逻辑上看起来后者是前者的一部分。
import导入
- import java.util.Date
- import java.util.* 导入java.util下的所有类
注意:如果导入俩个相同名称的类名时,需要用包名+类名来使用
继承(extends)
继承实现了代码的重用性,不用重复造轮子
继承的要点:
- 父类也称为超类,基类,派生类
- java只有单继承,而借口实现的多继承
- 如果一个类没有调用extends,那么他的父类是java.lang.Object
public class extendsTest {
public static void main(String[] args) {
Student3 stu = new Student3("田坤",18,"软件技术");
System.out.println(stu.name);
System.out.println(stu.grand);
stu.rest();
//instanceof运算符是在运行的指出对象是不是特定类的一个实例
System.out.println(stu instanceof Student3);
System.out.println(stu instanceof Person);
System.out.println(stu instanceof Object);
}
}
class Person{
String name;
int age;
public void rest(){
System.out.println("休息,休息");
}
}
class Student3 extends Person{
String grand;
Student3(){
}
Student3(String name,int age,String grand){
this.name = name;
this.age = age;
this.grand = grand;
}
public void study(){
System.out.println("学习使我快乐");
}
}
田坤
软件技术
休息,休息
true
true
true
方法的重写(覆盖)override
方法的重写需要符合下列要求:
- "==" :方法名,参数列表相等
- "<=" :返回值类型和异常声明类型,子类必须小于父类
- ">=" :访问权限,子类大于等于父类
class Person{
String name;
int age;
//人的普通休息
public void rest(){
System.out.println("我可以睡10个小时");
}
public Person returnPerson(){
return new Person();
}
}
class Student3 extends Person{
String grand;
Student3(){
}
Student3(String name,int age,String grand){
this.name = name;
this.age = age;
this.grand = grand;
}
//学生的休息
public void rest(){
System.out.println("我只可以睡7个小时");
}
public Student3 returnPerson(){
/*
Student3是Persom的子类所以返回值类型为Person的重写方法也可以,同样Student3类型自然也是可以的
但是如果返回值类型为Object则不行,Object的Person的父类
如果子类的修饰符为protected会报错,因为protected的访问权限小于public
*/
return new Student3();
}
}
-------------------异常声明类型暂时留着
Object类
object是所有类的父类,那么也就是意味着所有的类都继承了object中的方法,其中最常用的就是toString() 方法;
object中的equal方式是比的是对象(hashcode),而其实类重写的equal是比的是属性的值。
• ==:
• 比较两基本类型变量的值是否相等
• 比较两个引用类型的值即内存地址是否相等,即是否指向同一对象。
• equals() :
• 两对象的内容是否一致 (是对象不是变量)
多数equal是我们需要自己写的,
public class Student1 {
public static void main(String[] args) {
Student0 stu1 = new Student0("田坤",18);
Student0 stu2 = new Student0("宋旭东",18);
System.out.println(stu1.toString());
System.out.println(stu2.equals(stu2));
}
}
class Student0{
String name;
int age;
static String school="西安理工大学高等技术学院";
Student0(String name,int age){
this.name = name;
this.age = age;
}
//重写toString方法
public String toString(){
return name+"年龄:"+age;
}
//重写equals方法
public boolean equals(Object obj){
if(this==obj)
return true;
if(obj == null)
return false;
if(this.getClass()!=obj.getClass())
return false;
//以上部分是重写equal方法的必经之路
Student0 stu = (Student0)obj;
if(stu.name == name)
return true;
else return false;
}
}
super关键字
super是直接父类对象的引用,可以同super来访问父类中被子类覆盖的方法和属性
若构造方法的第一行没有写super()或者this(...),那么java里面会默认调用super(),含义是调用父类的无参构造函数
public class SuperTest {
public static void main(String[] args) {
new Super1();
}
}
class Super0{
public Super0(){
//super() 默认调用
System.out.println("我是父类");
}
}
class Super1 extends Super0{
public Super1(){
//super(); 默认调用
System.out.println("我是子类");
}
}
我是父类
我是子类
使用super()
public class SuperTest {
public static void main(String[] args) {
new Super1().Sing();
}
}
class Super0{
public void Sing(){
System.out.println("月亮之上");
}
}
class Super1 extends Super0{
public void Sing(){
super.Sing();//调用父类的Sing方法
System.out.println("可不可以");
}
}
月亮之上
可不可以
封装
作用:
- 提高代码的安全性
- 提高代码的复用性
- “高内聚”:封装细节,便于修改内部代码,提高可维护性
- “低耦合”:简化外部调用,便于调用者使用,便于扩展和协作
封装的实现——访问控制符
修饰符 | 同一个类 | 同一个包 | 子类 | 所有类 |
---|---|---|---|---|
private | * | |||
default | * | * | ||
protected | * | * | * | |
public | * | * | * | * |
类的属性的处理:
- 一般使用private权限
- 提供相应的get/set方法来访问相关的属性,这些方法都是用public修饰的,以提供对属性的赋值和读取功能(注意: boolean变量的get方法是以is开头){ 我们可以用过set方法规范我们所设置的值 }
- 用于本类的方法用private修饰,希望可以被别的类调用的用public。
public class dengzhuang {
public static void main(String[] args) {
fengzhuang fz = new fengzhuang();
fz.setAge(-15);
System.out.println(fz.getAge());
}
}
class fengzhuang{
private String name;
private int age;
private String school;
public int getAge() {
return age;
}
//检测用户所输入的年龄
public void setAge(int age) {
if(age<0)
System.out.println("请输入正确的年龄");
else this.age = age;
}
}
请输入正确的年龄
0
多态(polymorphism)
多态是同一种方法的调用,由于对象的不同可能有不同的行为,比如说调用人 “ 玩游戏 ” 的方法,有的人是英雄联盟,有人是王者农药,有人是连连看。
多态的要点:
- 多态是方法的多态,不是属性的多态(多态与属性无关)
- 多态存在三个必要条件:
- 继承
- 方法重写
- 父类引用指向子类对象
- 父类引用指向子类对象后,用该父类引用调用子类重写方法,此时多态就出现了。
public class GameTest {
public static void main(String[] args) {
Game game = new Game();
game.play();
playGame(new User1()); // Game game = new User1()
playGame(new User2());
}
//实现多态
static void playGame(Game game){
game.play();
}
}
class Game{
void play(){
System.out.println("我在玩游戏");
}
}
class User1 extends Game{
void play(){
System.out.println("我在玩英雄联盟");
}
}
class User2 extends Game{
void play(){
System.out.println("我在玩王者农药");
}
}
我在玩游戏
我在玩英雄联盟
我在玩王者农药
final关键字
- 修饰变量:被修饰的变量不可改变
- 修饰方法:该方法不可被子类重新,但可以被重载
- 修饰类:修饰的类不能被继承
抽象类_抽象方法(abstracrt)
使用abstract修饰的方法,没有方法体,只有声明,定义的是一种 “ 规范 ” ,告诉子类必须给抽象方法提供具体的实现
抽象类的使用要点
- 有抽象方法的类只能定义为抽象方法
- 抽象类不能实例化(new)
- 抽象类可以包含属性,方法,构造方法,但构造方法不能用来new实例,只能用来被子类调用
- 抽象类只能被继承
- 抽象方法必须被子类实现
public class AbstracatTest {
public static void main(String[] args) {
Person1 p = new Man(); //不能 Person1 p = new Person();
p.eat();
}
}
abstract class Person1{
abstract void eat();
}
class Man extends Person1{
@Override
//实现父类的eat()
void eat() {
System.out.println("我在吃米饭");
}
}
接口
接口是比抽象类更抽象,
声明格式:
【访问修饰符】 interface 接口名 [extends 父接口1,父接口2.....]{
常量定义;
方法定义;
}
定义接口的详细说明:
- 访问修饰符:只能是public或者默认
- 接口名:和类名采取相同的命名机制
- extends:接口可以多继承
- 常量:接口中属性只能是常量,总是 : public static final 修饰
- 方法:接口中的方法只能是: public abstract 。 省略的话,也是public abstrcat
要点:
- 子类通过implements来实现接口中的规范
- 接口不能创建实例,但可以用于声明引用类型变量
- 一个类实现了接口,必须实现接口中所有的方法,并且这些方法都是public的
- JDK1.7前,接口只能包好静态常量,抽象方法
- JDK1.8后,接口中包含普通的静态方法
内部类
内部类的分类:
- 成员内部类(非静态内部类,静态内部类)
- 匿名内部类
- 局部内部类(基本不用)
##非静态内部类(外部类使用非静态内部类和平常的类一样)
- 非晶态内部类必须寄存在一个w外部类对象里
- 非静态内部类可以直接访问内部类的成员,反之不行
- 非静态内部类内部不能有静态方法,静态属性,和静态初始化块
- 外部类的静态f方法,静态代码块不能访问非静态内部类,包括不能使用内部类定义变量,创建实例
成员变量访问的要点
- 内部类里面方法里面的局部变量:变量名
- 内部类属性:this.变量名
- 外部类属性:外部类名.this.变量名
public class InnerTest {
public static void main(String[] args) {
//创建非静态内部类
//内部类依附于外部类
Outer.inner oi = new Outer().new inner();
oi.run();
}
}
class Outer{
int age=10;
class inner{
int age=20;
void run(){
int age=30;
System.out.println("外部类的成员变量"+Outer.this.age);
System.out.println("内部类的成员变量"+this.age);
System.out.println("局部变量"+age);
}
}
}
外部类的成员变量10
内部类的成员变量20
局部变量30
##静态内部类
使用要点:
- 当一个静态内部类对象存在时,并不一定存在外部类对象,因此,静态内部类的实例方法不能直接访问外部类的实例方法。
- 静态内部类可以看作是一个静态成员,所以,外部类的方法可以通过 “ 外部类类名.名字 ”的方式去访问静态内部类的j静态成员,通过new 静态n内部类()访问内部类的实例。
public class InnerTest1 {
public static void main(String[] args) {
//静态内部类不依托于外部类的对象
Outer2.Inner2 oi = new Outer2.Inner2();
}
}
class Outer2{
static class Inner2{
}
}
##匿名内部类
public class NibuleiTest {
public static void main(String[] args) {
Demo demo = new Demo();
demo.runing(new AA(){
@Override
public void run() {
System.out.println("我好焦灼啊");
}
});
}
}
class Demo{
void runing(AA a){
a.run();
System.out.println("我也好焦灼啊");
}
}
interface AA{
void run();
}
我好焦灼啊
我也好焦灼啊
这次的编写中大量的学习了尚学堂的课程里面的知识,这篇博客更像是我在尚学堂学习java基础的笔记。在此先感谢尚学堂这个机构,而且我也是先开始写博客,而且自己的知识储备极其的少,这篇博客还是很多未涉及到的知识,或者是错误的知识,如果有人可以找到我的错误,希望你可以帮我指出,我会认真学习。