从零学点Java记录下过程。边听边敲的txt笔记的备份,督促自己尽快学习一下Java基础内容。
内容基本来自CSDN学院的课程《java核心技术精讲》以及在搜索引擎和《Java核心技术 卷1》中查的资料
目录
1.初识Java开发
1.class与public class
用.java文件定义类时
public class定义文件名必须与类名相同但是class可以不同。编译后产生的.calss文件都是与类名相同的
一个.java文件中只能有一个public class但可以有多个class
用javac 命令编译.java 后会生成多个.calss文件且与类名相同
2.主方法
一切程序的起点叫主方法(相当于main()函数)由JVM调用
public static void main(String args[]){
***
}
有主方法的类叫做主类 主类只能用public calss来声明(主类用来测试)
3.PATH与CLASSPATH与JAVA_HOME
PATH:
是系统定义的环境属性,用于定义所有的可执行程序路径,系统通过PATH找javac\java命令的位置
CLASSPATH:
指定编译好的类的位置,JVM通过CALSSPATH 来找类的位置。先在CALSSPATH找再去PATH路径中找。可设置为.表示当前目录
JAVA_HOME:
它指向jdk的安装目录,Eclipse/NetBeans/Tomcat等软件通过JAVA_HOME来找到JDK的目录
命令行:
set classpath=c:\
全局设置:
新建用户变量CLASSPATH 值为 .
只要是解释程序,必须要有CLASSPATH
2.Java变量与运算符
数字:
float x=10.2F;
double x=10.2;
int x=10;
long x=1L;
小数默认double
整数默认int
小转大自动转换,大转小要强制转换。
字符:
单引号声明的表示char
char可以保存中文 (UNICODE编码);
大写字母加32转小写
布尔:
boolean
两个值 true false;
String:
String 是一个类
双引号声明的字符串就是String类
可以用+号连接
逻辑运算符:
& && | || !
短路与&&
短路或||
switch支持的数据类型
最初:int char
1.5+:enum(枚举)
1.7+:字符串String
case满足是入口,执行到break为止。
字符串判断的时候大小写敏感
3.Java中的方法
1.方法基本定义
public static 返回值类型 方法名称 ([参数类型 变量,参数类型 变量,……]){
……代码
[return ……];
}
方法名称第一个单词的首字母小写,后面的单词首字母大写
主方法代码太多,封装成方法
复制粘贴重复的代码太多,封装成方法
2.方法的重载
方法的功能相似、相同,参数类型或者个数不同,则可以使用方法重载。
调用重载方法时,自动根据传递的参数个数和类型进行选择。
(取一样的名字就行了)
方法的返回值类型虽然可以不同,但是实际开发操作最好返回值相同。
附:Java命名规则
类名:首字母大写,通常由多个单词合成一个类名,要求每个单词的首字母也要大写,例如class HelloWorldApp;
接口名:命名规则与类名相同,例如interface Collection;
方法名:往往由多个单词合成,第一个单词通常为动词,首字母小写,中间的每个单词的首字母都要大写,例如:balanceAccount,
isButtonPressed;
变量名:全小写,一般为名词,例如:length;
常量名:基本数据类型的常量名为全大写,如果是由多个单词构成,可以用下划线隔开,例如:int YEAR, int
WEEK_OF_MONTH;如果是对象类型的常量,则是大小写混合,由大写字母把单词隔开。
3.递归略
递归可能会内存溢出。尽可能非递归。
4.面向对象
面向过程:只完成自己所需要的操作。缺少可维护性。
面向对象:模块化设计,方便代码的局部维护。
特点:
封装性:
保护内部的操作对外部不可见
继承性:
父类会干的事,子类也会干
多态性:
在一个范围内的定义改变\\?
三个阶段:
OOA:面向对象分析
OOD:面向对象设计
OOP:面向对象编程
面向对象思想学习的目标:
可以进行现实世界的抽象
可以进行合理的类的功能设计
1.类与的对象的定义
类:具备某一共性的群体集合。
对象:具体的一个个性描述。
对象所能具备的操作行为是有所属的类来决定的。
简单的说,抽象的是类,具体的是对象。
1.1 class关键字定义类
class ClassName{
//属性:
Type A;
//方法:
public void methodName(){
...
}
}
类显然是个引用数据类型。那么对象的产生定义语法:
声明并实例化:类名称 对象名称=new 类名称();//已经给他分配了内存空间
分布进行:
声明对象:类名称 对象名称=NULL;//还没分配
实例化: 对象名称=new 类名称();
注:
引用数据类型在开始使用前必须开辟空间,而基本数据类型可以直接赋值。
开辟空间的关键字:new
2.对象产生的内存分析
内存空间定义:
堆Heap:保存对象的具体属性信息;
栈Stack:保存堆内存的地址数值(可以理解为保存对象的名称);
引用数据类型必须对其进行内存分析:
Person Dean = new Person();
栈内存 堆内存
Dean地址XXX new→ name="Dean";age=0;
就是C中的指针。
只有New才会给他分配一个堆内存来存属性,没有New就不给他分。New就是来开辟堆内存空间。
Person Dean = NULL;并不会给他分配堆内存。
如果使用了没有实例化的对象:
NullPointerException异常。使用了没开辟堆内存空间的应用对象。
3.引用传递初步
简单的理解:多个名字(栈内存)指向统一个堆内存。
Person A=new Person();//实例化一个A
Preson B=A;//A的小名叫B
栈内存中会有两个指向统一个堆内存的地址。B就是一个指向A的堆内存地址的指针。
所以修改B也会影响A。
就算B也实例化了 那么B=A也会使B指向A的堆内存空间,并且之前B指向的内存空间变成垃圾空间(没有其他栈内存指向的话)。会被GC(Garbage Collector)回收并且释放。但是GC得执行时不确定的所以最好少产生垃圾空间。
5.深入分析类与对象
1.封装
为了保护类的属性,使其不能被随意修改,类的外部不能直接进行访问,所以安全性提高。
private关键字可以对属性实现封装
开发原则:
只要是类中的属性全部使用private封装
封装后的属性只能通过setA(),getA(),方法来访问(setter\getter方法)。
set中可以添加合法性判断。
2.构造方法与匿名对象
构造方法:
与类名称一样的方法,且没有返回值。
class Person{
public Person(){
//实例化的时候被调用(new)
}
}
//没定义构造方法的话,会自动加一个空的构造方法。
//就是C++的构造函数
构造方法可以用来初始化对象。可以省略一系列的setter和getter函数的调用。
构造方法也可以重载,这样初始化的时候随便可以传几个参数
重载的构造方法按照参数个数的升序排序。(Java可以省略参数吗?)//好像不可以.
构造过程:
加载类,开辟空间、赋值。
而构造方法是在最后一步。只有构造完成了,才会实现对属性的赋值。
匿名对象:
不给他起名字,用一次就变为垃圾空间。随时可能被GC回收。(没有栈内存指向的堆内存)
6.简单Java类
简单的小练习:
写个雇员类
包含:雇员编号,姓名,职位,基本工资等。
类的开发原则:
1.类名称必须要有实际意义。
2.类的所有属性必须使用private封装
3.所有封装的属性必须要setter/getter方法
4.类可以定义有若干个构造方法,但是必须保留有一个明确的无参构造函数
5.类中不允许出现任何的输出操作,所有的输出必须将数据返回给调用处输出
6.类中应该有一个可以取得对象完整信息的方法。暂时取名为getInfo
class Emp{//雇员
private int empno;//编号
private String ename;//姓名
private String job;//职位
private double sal;//薪水
public Emp(){
//实例化的时候被调用
}
public Emp(int eno,String ena,String j,double sal){
//赋值构造
setEmpno(eno);
setEname(ena);
setJob(j);
setSal(sal);
}
public String getInfo(){
return
"雇员编号:"+empno+"\n"
+"雇员姓名:"+ename+"\n"
+"雇员职位:"+job+"\n"
+"雇员薪水:"+sal+"\n";
}
public void setEmpno(int eno){
empno=eno;
}
public void setEname(String ena){
ename=ena;
}
public void setJob(String ejob){
job=ejob;
}
public void setSal(double esal){
sal=esal;
}
public int getEmpno(){
return empno;
}
public String getEname(){
return ename;
}
public String getJob(){
return job;
}
public double getSal(){
return sal;
}
}
public class Test{
public static void main(String args[]){
Emp dean=new Emp(609966788,"Dean","Coder",0.1);
System.out.print(dean.getInfo());
}
}
7.Java中的数组
所有开发都会使用到数组,但不会使用到这么复杂。笔试却爱考察复杂内容。
Java中数组属于引用数据类型,依然牵扯到内存的关系。
1.基本概念
声明并开辟数组:
数据类型 数组名称[] = new 数据类型[长度];
数据类型 []数组名称 = new 数据类型[长度];
new必然会给他分配堆内存
分步完成:
数据类型 数组名称[]=null;
数组名称=new 数据类型[长度];
使用数组:
数组名称[索引];
若索引超出范围(>=length),"ArryIndexOutOfBoundsException"异常,越界.
*数组对象.length取得数组长度.
for(int i=0;i<A.length;i++)
code.;
2.内存分析
与对象的内存分配几乎一样,区别就是普通对象通过属性名来访问,但数组用[索引]来访问.
所以也可以进行数组引用(同一块堆内存被不同的栈内存指向.)
int data2[]=data1;
这个赋值语句实际上是吧data2直接指向data1的堆内存.
3.数组静态初始化
数据类型 数组名称[]={值,值,...};//简化型
数据类型 数组名称[]=new 数据类型[]{值,值,...}//完整型
单独使用new 数据类型[]{值,值,...}就是匿名数组。
4.二维数组
二维表,行,列索引确定一个位置。
一般来说引用传递不需要返回值。写个排序练一下。
public class Test{
public static void swapArryElem(int arry[],int i,int j){//交换两元素
int temp=arry[i];
arry[i]=arry[j];
arry[j]=temp;
}
public static void slectSort(int data[]){//选择排序
for(int i=0;i<data.length;i++)
for(int j=i+1;j<data.length;j++){
if (data[j]<data[i]){//交换
swapArryElem(data,i,j);
}
}
}
public static void printArry(int arry[]){//输出数组
for(int i=0;i<arry.length;i++)
System.out.print(arry[i]+" ");
System.out.print("\n");
}
public static void reverseArry(int arry[]){//逆序数组
int center=arry.length/2;
int head=0;
int tail=arry.length-1;
for(int i=0;i<center;i++){//两边往中间交换
swapArryElem(arry,head++,tail--);
}
}
public static int[] iniIntArry(){//初始化数组
return new int[]{20,10,3,70,22,345,12,43};
}
public static void main(String args[]){
int data[]=iniIntArry();
slectSort(data);
printArry(data);
reverseArry(data);
printArry(data);
}
}
5.与数组有关的类库
数组拷贝:System.arraycopy(源数组,源数组起始点,目标数组,目标数组起始点,长度);
数组排序:java.util.Arrays.sort()
6.对象数组,及其内存分析
动态初始化:类名称 对象数组名称[]=new 类名称[长度];
静态初始化:类名称 对象数组名称[]=new 类名称[]{实例化对象,实例化对象....};
![](https://i-blog.csdnimg.cn/blog_migrate/45e031e58ef51d2050763328e12f0b9a.png)
8.String类剖析
String并不是基本数据类型,属于一个类.
但是可以直接赋值
1.实例化两种方式
直接赋值 String 名称="内容";
利用构造方法 public String(String s);
String str=new String("Hello World!");
2.String的相等比较
利用String类中的方法
public boolean equals(String str);(简)
注:
"=="两端写String类的对象名称,比较的是他们对象的堆内存地址是不是相同.
比较内容必须用equals();
从根本来分析还是因为String他不是一种基本数据类型,是一个引用数据类型,一个String类对象的对象名包存在栈内存中,其内容实际上是一个指向该对象在堆内存中的地址.
3.String匿名对象
""声明的数据不是普通的变量,而是一个String类的对象.
例如可以:"hello".equals(str);
而String 字符串对象="字符串"实际上是给匿名对象起了个名字
对可能为NULL的对象进行判断的时候最好用短路&&判断是否为NULL
或者 if("hello".equals.(str))而不是把常量"hello"放在后面.[当str为NULL时候,str.equals.("hello")非法]
4.String类两种实例化对象的区别
直接赋值实例化:
-关于对象池(Object Pool)
Java设计的过程,为了方便开发,针对几个特殊的类使用了共享设计的思路.而String类属于这其中的一员.这种设计思路是Java自己的支持,而且只针对于直接赋值的情况.
在使用直接赋值实例化String类对象的操作之中,会将其保存在对象池之中,如果再有其他的字符串对象也采用直接赋值的情况进行实例化,那么他回去看看对象池有没有一样的对象,有的话就不给他开辟新的堆内存空间了,直接引用他就行了,这样就会出现,先后赋值两个内容相同的字符串对象,他们指向的是同一个堆内存地址.这样的设计就是共享设计模式.
利用构造方法实例化:
String str=new String("Hello!");
在这个实例化过程中,首先有个匿名对象"Hello",然后new会开辟一片堆内存空间,并且构造方法将"Hello!"保存到这块堆内存中,str在栈内存中指向这块堆内存,这样一开始的匿名字符串"Hello!"就变成了一块垃圾内存,等待GC回收.
使用构造方法是不会自动入对象池的.如果非要入池则需要用户手工入池.
利用public String intern()方法进行入池操作:
String str=new String("Hello!").intern();//手工入池
小结:直接赋值更简单,只会开辟一块堆内存,并且会自动入池.而构造方法会开辟两块堆内存并且会产生一块垃圾,且不能自动入池.
5.字符串修改分析
-字符串一但赋值不能改变;
但考虑如下代码及结果:
String str="Hello ";
str +="World";
System.out.println(str);//结果为Hell World;
据此进行内存分析:
str指向"Hello"
开辟"World"
开辟"Hell World"
str指向"Hell World"
"Hello"和"World"成为垃圾空间.
可以发现整个操作过程中,对象内容没有发生改变,而是str的引用发生改变;
据此,循环修改String的操作禁止在开发中出现.频繁修改String虽然可以实现,但是会产生大量的垃圾空间.
总结:
1.String都采用直接赋值
2.判断内容相等请用equals();常量放前面.
3.不要频繁修改他
9.String类常用方法
目标:将String类的常用方法全部记下来,包括 :
方法名称
参数作用\类型
一个成熟的开发语言,需要有大量的开发类库,Java的开发类库出了本身提供的JDK Doc之外还有大量的文档.可惜文档只有英文或者日文.
虽然有汉化版,最好还是看看英文的练练吧.
一般来说,开发代码需要动态查询文档,但是常用的类库必须要牢记常用的东西.
java.lang里有String的文档
1.字符与字符串
Constructors(构造方法):
public String(char[] value)
public String(char[] vale,int offset,int count)
Methods(普通方法):
public char charAt(int index)
public char[] toCharArry()
验证代码:
public class Test{
public static void main(String args[]){
String str="hello world!";
//验证1
char a=str.charAt(0);//h
char b=str.charAt(1);//e
char c=str.charAt(str.length()-1);//!
System.out.println(a);System.out.println(b);System.out.println(c);
//验证2
char chararry[]=str.toCharArray();//h,e,l,l....
for (int i=0;i<chararry.length ;i++ ){
System.out.print(chararry[i]);
}
System.out.print('\n');
//验证3
chararry[0]='H';
str=new String(chararry);//Hello world!
System.out.println(str);
str=new String(chararry,0,5);//Hello
System.out.print(str);
}
}
2.字节与字符串
Constructors(构造方法):
public String(byte[] bytes)\\将字节数组变为字符串
public String(byte[] byers,int offset,int length)\\部分
Methods(普通方法):
public byte[] getBytes()\\字符串变为字节数组
public byte[] getBytes(String charsetName) \\编码转化
str=new String(str.getBytes("ISO-8859-1"));
3.字符串比较
已经使用过equals()方法,大小写敏感的比较.
boolean equalsIgnoreCase();方法大小写不敏感
int compareTo(String anotherString);比较字符串大小
int compareToIgnoreCase(String str);不区分大小写比较大小
compareTo()返回小于零 等于零 大于零.
实际是对编码 进行相减得到的.
4.字符串查找
public boolean contains(String s) 判断某一个字符串是否存在
public int indexOf(String str) 取得某个子串的位置 找不到返回-1(这个是从头开始)
public int indexOf(String str,int fromIndex)从指定索引位置开始检索子串
public int lastIndexOf(String str) 从后往前找子串,找不到返回-1
public int lastIndexOf(String str,int fromIndex)从后往前从指定位置开始找,找不到返回-1
public boolean startsWith(String prefix)是否以某个字符串开头
public boolean startsWith(String prefix,int toffset)从指定位置开始判断是否以某个字符串开头
public boolean endsWith(String suffix)是否以某个字符串结尾
5.字符串的截取
通过完整字符串截取子字符串.
public String substring(int beginIndex)指定位置到结尾
public String substring(int beginIndex,int endIndex)//begin到end
6.字符串转换
将指定字符串替换为其他内容
public String replaceAll(String regex,String replacement)全部替换
public String replaceFirst(String regex,String replacement)首次遇到替换
7.字符串拆分
可以将以个字符串根据指定内容拆分
public String[] split(String regex);全部拆分
public String[] split(String ,int limit);部分拆分
注意:当方法描述里有regex(正则)注意使用转义.如"."应该改写为"\\."
8.其他操作方法
public int length(String s)取得长度\String 中是length()方法,而数组中是.length属性
public boolean isEmpty()是否为空.注意null不是空
public String toLowerCase()转小写
public String toUpperCase()转大写
public String trim()去掉左右空格
public String concat(String s)连接相当于+
测试代码:
public class Test{
public static void main(String args[]){
String str="Hello world!";
//equals()测试
System.out.println("hello world!".equals(str));
System.out.println("Hello world!".equals(str));
//equalsIgnoreCase()测试
System.out.println("hello world!".equalsIgnoreCase(str));
//compareTo()/compareToIgnoreCase()测试
System.out.println("hELLO world!".compareTo(str));
System.out.println("HELLO world!".compareTo(str));
System.out.println("HELLO world!".compareToIgnoreCase(str));
System.out.println("A".compareTo("a"));//大写字母的ASCII小
//contains()测试
System.out.println(str.contains(str));
System.out.println(str.contains("hell"));
//indexOf()测试
System.out.println(str.indexOf("llo"));
System.out.println(str.indexOf("lelo"));
System.out.println(str.indexOf("llo",2));
System.out.println(str.indexOf("llo",3));
//lastIndexOf()测试
System.out.println(str.lastIndexOf("llo"));
System.out.println(str.indexOf("lloe"));
//starsWith()/endsWith()//测试
System.out.println(str.startsWith("Hell"));
System.out.println(str.startsWith("hell"));
System.out.println(str.startsWith("ell",1));
System.out.println(str.endsWith("ld!"));
//substring()//测试
System.out.println(str.substring(6));
System.out.println(str.substring(6,7));
//replaceAll()/replaceFirst()测试
System.out.println(str.replaceAll("o","0"));
System.out.println(str.replaceFirst("o","0"));
//split()测试
String[] result=null;
String str1="192.168.1.1";
result=str1.split("\\.");
for(int i=0;i<result.length;i++)
System.out.println(result[i]);
System.out.println("\n\n\n");
result=str1.split("\\.",3);
for(int i=0;i<result.length;i++)
System.out.println(result[i]);
//length()测试
System.out.println("Dean真的是太菜了233333".length());
//isEMPty()测试
System.out.println(str.isEmpty());
System.out.println("".isEmpty());
//toLowerCase()测试
System.out.println(str.toLowerCase());
//toUpperCase()测试
System.out.println(str.toUpperCase());
//trim()测试
System.out.println("["+" aa ".trim()+"]");
//concat()测试
System.out.println("a".concat("bb"));
}
}
10.this关键字
this是一个灵活的关键字,他不会明确的表示出一个任何固定的概念.
1.调用本类属性
类里面是直接进行成员调用,但是很多时候为了说明是调研的本类属性,可以用this关键字.
在以后调用本类属性的时候必须严格按照 this.属性 的形式调用
2.调用本类方法
调用本类其他方法: this.方法名();
调用本类其他构造方法:this():
该语句只能放在构造方法的首行.不在构造方法首行或者在普通方法中都不行.
禁止出现循环调用this();
调用this()来构造可以消除大量重复代码.
3.表示当前对象
在一个类中,可以生成若干的对象,那么每一个对象都可以调用类中的方法.为了能够清晰的区分出当前正在操作类的方法是哪一个.可以用this来区分
this的值是随着调用者的变化而变化的。谁用this就是谁。
总结
以后开发中记得this.
11.引用传递实例分析
引用传递是Java的精髓,分析三个例子
1.引用传递进阶分析
1.
代码:
class Demo{
private int num=10;
public Demo(int num){
this.num=num;
}
public void setNum(int num){
this.num=num;
}
public int getNum(){
return this. num;
}
}
public class Test{
public static void main(String args[]){
Demo demo=new Demo(100);
fun(demo);
System.out.println(demo.getNum());//30
}
public static void fun(Demo temp){
temp.setNum(30);
}
}
内存分析:
2.
代码:
public class Test{
public static void main(String args[]){
String str="hello";
fun(str);
System.out.println(str);//hello
}
public static void fun(String temp){
temp="world";
}
}
内存分析:
3.
代码:
class Demo{
private String msg="hello";
public Demo(String msg){
this.msg=msg;
}
public void setMsg(String msg){
this.msg=msg;
}
public String getMsg(){
return msg;
}
}
public class Test{
public static void main(String args[]){
Demo demo=new Demo("world");
fun(demo);
System.out.println(demo.getMsg());//HELLO
}
public static void fun(Demo temp){
temp.setMsg("HELLO");
}
}
内存分析:
总结:在使用引用数据类型进行引用传递的时候,一定要了解其内存分析。而String类,虽然是引用数据类型,但由于直接赋值这一特性的存在,其表现上往往与非引用数据类型相似。总之,了解内存分析就会减少出错。
2.对象比较
所谓对象比较指的是判断两个对象是否相等。实际上,通过对对象的地址进行比较,若相等则可判断两个对象相等,但是有时候需要比较内容的时候就要对其内容的每一项属性进行比较。
一般来说比较功能由对象自己来完成,即在对象定义里,定义一个比较函数。这里需要注意的是,在A类里定义比较方法,接受另一个A类对象,则这时可以直接访问私有属性。
所以在写某个类的对象比较方法只需要考虑:1.引用是否相同(相同直接返回) 2.内容比较
3.引用传递实际应用
表名称=类名称
表的字段=类属性
表的一行=一个实例化对象
表的多行记录=对象数组
表的外键 = 引用关系设置
引用的关系可以描述出不同类之间的引用关系。任何事物都可以进行这样的抽象。
这种设计模式叫做合成设计模式。
12.static关键字
static关键字是定义属性及定义方法的关键字。
1.利用static定义属性
为了避免冗余,可以通过static关键字实现将属性定义成为公共属性。
static公共属性的内容保存不会在栈中,而是在全局数据区中。
内存分析:
对于static定义的属性可以直接通过类名称调用,所以这样的属性也被称为类属性。
特别注意:
所有的非static属性都是在对象实例化的时候才会进行内存的分配。
所有的static属性可以在没有实例化对象产生的情况下直接使用。
虽然static的属性定义在了类之中,但是他是完全独立的,不受实例化对象的控制。
在90%情况下,都会使用非static属性,只有在极少数情况下才会考虑使用static。
简单JAVA类与数据表一致,而数据表没有公共的部分所以一般用不到static
2.利用static定义方法
static定义的方法和属性一样,可以直接通过类名称来进行调用。
但是注意:
所有非static可以调用static属性或者非static属性
但是static不能调用非static的属性或者方法
原因很简单,static若是调用非static此时feistatic可能还没有被实例化。
一般的方法都是非static,若是一个类没有任何的属性那么他就不需要定义非static方法只需要定义static方法。
因为类中没有属性,那么就不需要来使用堆内存,直接定义static方法,通过类名直接调用即可。
3.观察主方法
在主类中直接定义方法一定要定义成static,因为main是static不能调用非static
但是我们很少在主类中写方法,大部分方法都是封装在类中,通过实例化对象来调用。
Java中的主方法可以说是历史上最长的:
public:访问权限,表示所有操作者都可以看见
static:输入类名称可以直接调用
void:表示主方法是一切的起点
main:是系统定义的方法名称
String args[]:表示程序输入时的输入参数\就是解释时候输入的参数
例如在命令行中输入java Test 100
这个参数100就被args接收。
4.static应用案例
static在一些系统类库上会大量出现,static有一个最好的特点在于,公共属性,所有对象都可以对其进行修改。
范例:
-设计一个对象统计属性
-为属性自动命名
[可以避免为空,避免重复]
13.代码块
代码块在实际工作中很少见。作为知识点了解即可。
代码块分为四种:普通代码块、构造块、静态块、同步代码块(多线程)。
普通代码块:
在方法中用{}框起来的代码块,几乎用不上
构造块:
将普通代码块中的内容提取到类中使用,叫构造块
构造块先于构造方法执行,并且每一次实例化都会执行一下构造块
这也是很少用的
静态块:
如果代码块中使用static关键字来定义,那么这样的代码块就称为静态块
·形式1:在非主类中定义静态块
·形式2:在主类中定义静态块
一般情况下可以用静态块为static属性进行初始化
[spring框架准备]
静态块是优先于主方法来执行的JDK1.7之前是可以用静态块代替主方法。
14.内部类
1.内部类的概念
类的组成永远都是两点:成员+方法。内部类是指在一个类的内部可以继续嵌套其他类结构的一种形式代码。
观察内部类代码:
class Outer{//外部类
private String info="hello world";
class Inner{//内部类
public void print(){
System.out.println(info);
}
}
public void fun(){
Inner in = new Inner();
in.print();
}
}
public class Test{
public static void main(String args[]){
Outer out=new Outer();
out.fun();
}
}
类本身的核心组成就应该是属性和方法,如果引用内部类就破坏了之一结构,这是内部类带来的问题。但是内部类也有好处。
其实内部类最大的好处就是可以对外部类的私有成员进行直接访问,可以节省很多代码。(外部内也可以直接访问内部类)
单独实例化内部类:
外部内名称.内部类名称 对象名称 = new 外部类.new 内部类();
但是如果外部类使用private定义私有内部类则无法在外部单独实例化内部类。
对于内部类的概念与它的结构有个先期的认识即可。
2.static定义内部类
利用static定义的方法是不受类控制的。如果用static定义了内部类那么就相当于内部类变为了一个外部类。但此时内部类只能访问外部类的static属性
那么既然static的内部变为了外部类,那么就可以被其他内操作。
外部类名称.内部类名称 对象名称=new 外部类.内部类();
[相当于把内部类的名字改成了 “外部类名称.内部类名称”]
以后见到程序类库中出现有“xxx.xxx”就表示内部类。
3.方法中定义内部类
理论上内部类可以在任何位置上定义,包括:代码块、类中、方法中。
但注意的是JDK1.8之前,访问外部类的属性必须要在外部类属性前加上final
先看明白内部类的代码即可,暂时不考虑如何去使用。
15.简单Java类深入
1.数据表与简单Java类
简单Java类是整个项目开发的灵魂所在,他有自己严格的开发标准,而最为重要的是它需要与数据表完全对应
目前要求可以完成以下两类操作:
一:可以根据数据表定义的结构关系进行数据以及引用的设置;
二:根据数据表的结构可以去取出所需要的数据;
用一个简单的数据表结构实现这样的转换:dept、emp
开发要求如下:
1.数据表及字段:
雇员表(emp):empno、ename、job、sal、comm、mgr、deptno
部门表(dept):deptno、dname、loc
2.数据的操作要求:
根据表结构完整的设置雇员、经理、部门的关系;
可以完成以下内容的输出:
1.可以输出一个雇员的完整信息,包括雇员的领导、以及所在的部门信息;
2.可以输出一个部分的完整信息,以及这个部门的所有雇员信息,以及这个雇员的领导信息
第一步:写出基本字段的映射转换
第二步:设计关系字段
本程序存在两个关系:
自身关联:mgr字段,mgr也是一个雇员
外键关联:deptno字段
关系字段是要单独写setter 和 getter的
第三步:执行数据操作
1.设置数据关系
根据表结构设置数据
准备好所有的独立的类对象
设置彼此关系
2.根据数据表结构利用关系取得数据
练习代码如下:
//(emp):empno,ename,job,sal,mgr,deptno
//(dept):deptno,dname,loc
class Emp{//雇员类
private int empno;
private String ename;
private String job;
private double sal;
//基本字段setter&getter省略
//设计关系字段
private Emp mgr;//雇员领导,一个雇员对应一个领导
private Dept dept;//一个雇员属于一个部门
//写基本字段时完成:
public Emp(){}
public Emp(int empno,String ename,String job,double sal){
this.empno = empno;
this.ename = ename;
this.job = job;
this.sal = sal;
}
public String getInfo(){
return "编号:" + this.empno +
",姓名:" + this.ename +
",职位:" + this.job +
",工资:" + this.sal;
}
//设计关系字段时完成:
public void setMgr(Emp mgr){
this.mgr=mgr;
}
public Emp getMgr(){
return this.mgr;
}
public void setDept(Dept dept){
this.dept=dept;
}
public Dept getDept(){
return this.dept;
}
}
class Dept{//部门类
private String dname;
private String loc;
private int deptno;
//基本字段setter&getter省略
//设计关系字段
private Emp [] emp;//一个部门有多个雇员
//写基本字段时完成:
public Dept(){}
public Dept(String dname,String loc,int deptno){
this.dname=dname;
this.loc=loc;
this.deptno=deptno;
}
public String getInfo(){
return "部门:" + this.dname +
",编号:" + this.deptno+
",位置:" + this.loc;
}
public void setEmp(Emp [] emp){
this.emp=emp;
}
public Emp [] getEmp(){
return this.emp;
}
}
public class Test{
public static void main(String args[]){
//执行数据操作
//1.设置数据关系
//1.1准备独立对象
Emp empa=new Emp(1111,"Amy","worker",100.00);
Emp empb=new Emp(1112,"Emma","worker",110.00);
Emp empc=new Emp(1113,"Dean","leader",120.00);
Dept depta=new Dept("School","XUANCHENG",110);
//1.2设置彼此关系
empa.setMgr(empc);
empb.setMgr(empc);
empa.setDept(depta);
empb.setDept(depta);
empc.setDept(depta);
depta.setEmp(new Emp[]{empa,empb,empc});
//2.利用关系取得数据[验证]
System.out.println(empa.getInfo());
System.out.println("\t领导信息:"+empa.getMgr().getInfo());
System.out.println("\t部门信息:"+empa.getDept().getInfo());
System.out.println("==========================");
System.out.println(depta.getInfo());
System.out.println("雇员信息:");
for(int i=0;i<depta.getEmp().length;i++){
System.out.println(depta.getEmp()[i].getInfo());
if(depta.getEmp()[i].getMgr()!=null)
System.out.println("\t领导信息:"+depta.getEmp()[i].getMgr().getInfo());
}
}
}
2.双向一对多
[用户,课程,考试成绩]例子比较简单,基本差不多,代码就没敲了
这里需要注意,用户-课程表中,除了外键意外还有其他信息,那么就需要将这张表单独映射为一个Java类
3.多对多映射
[管理员-角色-组-权限]例子
按照第一个例子的步骤来就可以了
16.继承性
1.继承问题的引出
继承是面向对象中的第二大主要特点,其核心的本质在于:可以将父类的功能一直沿用下去
为什么需要有继承?思考两个类:人、学生。学生也是人,可以继承人的所有特点。
2.继承的实现
实现语法:
class 子类 extend 父类{}
需要注意点名词概念
子类:也被称为派生类
extends:虽然从本质上来讲属于继承概念,但是其翻译是扩展、扩充的意思。
父类:是指Java超类(super calss)
class Person{
private String name;
private int age;
public Person(){}
public Person(String name,int age){
this.name=name;
this.age=age;
}
public void setName(String name){
this.name=name;
}
public String getName(){
return this.name;
}
public void setAge(int age){
this.age=age;
}
public int getAge(){
return this.age;
}
public String getInfo(){
return "name:"+this.name+
",age:"+this.age;
}
}
class Student extends Person{//Student 是Person的子类
private String school;
public void setSchool(String school){
this.school=school;
}
public String getSchool(){
return this.school;
}
}
public class Test{
public static void main(String args[]){
Student stu=new Student();
stu.setAge(20);
System.out.println(stu.getInfo());
stu.setSchool("PKU");
System.out.println(stu.getSchool());
}
}
发现一点,子类在继承了父类后,对于父类支持的方法和属性一个不少的保持着。并且可以继续进一步扩充自己的属性与方法。
简单的说,在父类不能够修改的情况下,可以使用继承拿来进行扩充
3.继承的限制
虽然继承的核心目的在于扩充已有功能,但是在使用中其实也是有控制的。
限制1:
Java不允许多重继承,也就是说一个子类只能继承一个父类。
[C++支持多继承]
虽然Java不允许多重继承,但是允许多层继承。
calss C extends A,B{}//不允许
calss C extends A{}
calss C extends B{}//允许
实际开发中,不要超过三层。
限制2:
子类在继承父类之后,会将全部结构继承,但是对于私有操作属于隐式继承,而所有非私有操作属于显式继承
简单的说就是子类不可以直接去访问父类的私有属性,但是可以通过父类的方法来间接操作,或者无法操作。
限制3:
在实例化子类对象时会默认调用子类的无参构造方法,但是在执行子类构造前会自动实例化父类对象。
如果非要为子类加上一个调用父类构造的标记,则可用super();来表示调用父类构造。
在父类没有提供无参构造的时候,就得使用super(参数);
也就是说,可以通过super();来调用父类的有参构造,而且必须是构造器中的第一个语句
[这里出现了个问题this()和super()哪个在前面的问题,答案是这两个语句不能同时出现。]
总结:
1.继承的好处就是进行功能的扩充,并且Java只支持单继承
2.子类实例化对象时一定要先实例化父类对象,而后在实例化子类自己的对象
对象的初始化顺序:首先执行父类静态的内容,父类静态的内容执行完毕后,接着去执行子类的静态的内容,当子类的静态内容执行完毕之后,再去看父类有没有非静态代码块,如果有就执行父类的非静态代码块,父类的非静态代码块执行完毕,接着执行父类的构造方法;父类的构造方法执行完毕之后,它接着去看子类有没有非静态代码块,如果有就执行子类的非静态代码块。子类的非静态代码块执行完毕再去执行子类的构造方法。总之一句话,静态代码块内容先执行,接着执行父类非静态代码块和构造方法,然后执行子类非静态代码块和构造方法。
注意:子类的构造方法,不管这个构造方法带不带参数,默认的它都会先去寻找父类的不带参数的构造方法。如果父类没有不带参数的构造方法,那么子类必须用supper关键子来调用父类带参数的构造方法,否则编译不能通过。
17.覆写
1.方法覆写
当子类定义了与父类中完全一样的方法时(名称,参数,返回值)。就成为方法覆写。new一个谁,就用谁的方法。
覆写的意义:
保留方法名称,但是又需要对功能进行扩充的时候使用覆写。
覆写时可以用super来调用父类的方法:
this与super调用方法的区别:
this.方法先找本类,没有则使用父类继承的。
super.方法不查找本类,直接去父类的方法。
覆写的要求:
被覆写的方法不能拥有比父类给为严格的访问控制权限。
private<defult(friend)<public. 只能更开放。
而且private修饰的方法不能被覆写。
实际之中95%的方法都是public
注意覆写与重载的区别:
覆写(override) 重载(overloading)
子类中 一个类中
名称,参数,返回值相同 名称相同即可,参数类型,个数是不同的
有权限要求 没有权限要求
特别注意:虽然重载可以使返回值类型改变,但是原则上来说一般不建议改变返回值类型。
2.属性覆盖
当子类定义了与父类属性名称相同的属性是就称为属性的覆盖。这个没什么用,因为private属性是没办法覆写的。