JavaSE知识一波整理

要相信一切都是java游戏规则~

高琪老师的视频不错:https://www.bilibili.com/video/av47103781/?p=12

李刚的书写的也行,倒是发个闲鱼链接转手,哈哈~

要想看一些源码,首先得懂一个类叫内部类,这个在源码中经常出现:

1.内部类

在一个类中声明另一个类,但是在类中的方法中调用内部类与调用普通类没啥区别,但是在使用某个内部类,创建一个内部类对象。

例如下面介绍两个一个小例子

记住,内部类的所有属性外部类都可以访问到,你需要先创建它,对它进行调用。同样内部类也可以访问外部类的所有属性和方法。如果内部类与外部类的属性有相同的话,可以在内部类中使用 OuterClass.this.propertyName,来指明外部类的某属性,使用this.propertyName来指明内部类的某属性值。

 还要记住一点,非静态内部类是要寄生在外部类,需要有外部类的实例来创建内部类,所以在外部类的静态方法中,不能直接new 一个非静态内部类,而是需要先有外部类实例然后 外部类.new 内部类()的类似形式。如下所示

而单纯的是会报错的

 

下面是一个内部类调用外部类的例子:

前面说了一堆非静态内部类,下面说点静态内部类,静态内部类可以包含自己的非静态成员,但静态方法或者普通方法(实例方法)只能访问外部类的类成员静态成员,不能访问实例变量。主要原因是静态内部类持有的是外部类的类引用,而不是对象引用,而非静态内部类持有的是外部类的对象引用,所以可以访问到外部类的实例对象。

在外部类调用静态内部类的静态变量,可以使用静态内部类名称.静态成员名。也就是不需要依赖于外部类的实例引用。

除类里面可以添加内部类,接口里面也可以定义内部类,且默认修饰符为public static.也就是静态内部类。但是这种使用场景还是很少的。

【注记】:省略访问控制符的内部类默认修饰符为default ,也就是同包可见。如果在外部类以外的类创建非静态内部类实例,可以仿照下面格式

Out.In in = new Out().new In();//Out是外部类名,In是内部类名。

内部类声明都是一样的   外部类名.内部类名,而在创建内部类时有所不同,非静态需要有外部类实例引用,而静态可以不用外部类实例如下:

Out.In in = new Out.In();//Out是外部类名,In是静态内部类名。

如果是定义一个非静态内部类的子类,该子类可以是单独的类文件,此时调用时,也需要有子类的父类的外部类实例引用。哈哈绕口令。

不存在外部类的子类来重写外部类的内部类的情况!

局部内部类 在方法中定义内部类 局部内部类只在方法里面有效,方法外都看不到,所以也就无所谓的访问控制符。使用情况较少。

匿名内部类

创建匿名内部类时会立即创建内部类的实例。匿名内部类不能重复使用。匿名内部类常见的说法是钩子,Hook。

匿名内部类必须继承一个父类或实现一个接口,但最多只能继承一个父类,或实现一个接口。

【规则】匿名内部类不能是抽象类

匿名内部类不能定义构造器,因为没有名字也就没有构造器,但是可以使用初始化块来完成构造器需要完成的事情。

但是如果是继承一个抽象类的时候,还是匿名内部类还是可以使用有参构造器,此处的构造器都是抽象类声明的那些。如果实现接口,默认有一个无参构造器。

如果局部内部类想使用方法内声明的其他变量,可以直接调用,java 8之前的版本需要使用final修饰该变量,在java 8 及以后不用显示的声明final,可以直接调用,编译器默认给你带个final修饰符。

所以该变量在初始化后,你不能修改该参数。


初始化块的使用规则

创建对象实例时首先调用初始化块,然后调用构造函数,初始化块如果多个,需要按照顺序一个一个调用,初始化块里面可以定义局部变量,也可以使用一些循环条件等语句。但是没有名字,如果加修饰符只能加static修饰符。

普通初始化块与声明实例变量的作用非常相似,也是按照顺序执行。在使用初始化块对类里面的实例变量进行赋值时,不能指明类型,声明类型标在实例变量上,如果初始块里又声明变量类型,则为局部变量,不能影响类的实例变量的值。

下面一个小例子来看清static 和非static 的调用顺序

反正static 的先调用,加载后,下次创建对象就无需调用。

而每次创建对象时,不需要执行static 初始化块,但是会反复的执行非静态代码块和构造函数。从根父类到当前类。我感觉讲的挺仔细的,希望读者听懂。


第9章泛型

泛型就是在定义类、接口、方法时使用类型形参,这个类型形参(也称泛型)将在声明变量、创建对象、调用方法时动态得指定。

Java5的时候开始支持泛型。注意在自定义的泛型类中,构造方法不需要增加泛型声明,直接一个类名即可,但是在创建该类市还是需要带泛型声明,Java7的时候支持菱形语法,可以省略尖括号里面的类型实参。

9.1从泛型类派生子类

在继承泛型类的时候如果子类名称后面没有尖括号,而泛型类有尖括号,则泛型类必须传入类型实参,否则报错,有两种修改方式,子类和泛型类同时没有尖括号,或者同时带有尖括号。

9.2并不存在泛型类

你可以当做对某个类里面的某些要使用的类型的抽象,就是该方法处理很多类型的步骤是一样的。在静态方法、静态初始化块或者静态变量的声明和初始化中不允许使用泛型形参。

9.3类型通配符

如果把泛型形参用到List 这种集合上时,则需要记住,List<String> 与List<Object>并不存在子类父类的关系。

但是数组String[]确实是Object[]的子类。

记住List<?>中?代表任意类型 是所有List<***类>的父类,哈哈,根据需要也可以改成你在声明泛型的类的形参名称如,List<T>。如果声明泛型的类在使用时未给传入类型实参,则默认为Object 。由于List<?>虽是父类,但是没有指定元素,所以不能直接向里面添加元素,所以一般都是形参声明。当你调用时系统就知道你的实参类型了。唯一例外的添加对象是null,因为null是所有引用类型的实例。

来一个小例子,稍微解说:

 

9.3.1设定类型通配符的上限 使用? extends **类

9.3.2设定类型通配符的下限 使用? super **类(可以使用add方法)

9.3.3 可以设定需要是Number类或其子类,并必须实现java.io.Serializable接口

public class Apple<T extends Number & java.io.Serializabel>

9.4泛型方法 java8改进的类型推断:可以根据赋值情况,推断。就是在方法的返回值前加<T>,声明一下使用的泛型,多个之间用,隔开。

9.5擦除和转换

 

9.6泛型与数组

数组形式通常为 类型名[ ]来声明,可以此处赋值时,可选择类型的子类进行赋值,例如截图,Outer.Inner的父类是Object ,所以可以将Inner 数组赋值给Object[ ]。

foreach 通常是用来显示数组元素的函数,当数组元素类型为引用类型,对数组元素可以进行修改。如果是基本类型,则对原数组无影响。

怎么样创造无限的数组?可以使用Object+强制类型转换搞定,因为所以引用对象的父类都是Object。进行强制转换就可以访问数组元素

输出结果:

9.7总结


第8章Java集合

Java 集合分为Set、List、Queue(Java 5)、Map.

在Java5 之前会认为集合全是Object类型;从Java 5增加泛型后,Java集合可以记住容器中的对象类型。有util包下的集合类,也有支持多线程并发的集合类。

Collection 大的接口

Collection定义两个子接口,没有实现类。两个子接口分别是Set接口、List接口

Set 接口下的类   HashSet   LinkedHashSet   TreeSet

List接口下的类    ArrayList   LinkedList            Vector

Map接口有点独立哟 类有HashMap TreeMap Hashtable

Collections工具类


第18章 类加载机制与反射(开源框架不都是用这个很嗨皮嘛?)

来一个简单的使用反射小例子

调用 newInstance方法必须是有一个无参构造器且访问控制符要满足可见(public 或者default

三种获取class的方法

		Class clazz = Person.class;//加载class文件,也就相当于获取了该文件里面定义的所有内容
		//运行时类的对象
		Person p = new Person();
		Class clazz2 = p.getClass();
		
		//通过Class 的静态方法获取
		String className = "com.ligang.reflecttest.Person";
		Class clazz3 = Class.forName(className);

多样获取


package com.ligang.reflecttest;

public class Creature<T> {
	public double weight;
	public void breath(){
		System.out.println("呼吸");
	}
}


//自定义注解

package com.ligang.reflecttest;

import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
	String value();
}


//自定义接口
package com.ligang.reflecttest;

import java.io.Serializable;

public interface MyInterface extends Serializable {

}


//Person类
package com.ligang.reflecttest;
@MyAnnotation(value="ligang")
public class Person extends Creature<String> implements Comparable,MyInterface{
 public int age;
 private String name;
 int id;
 @MyAnnotation(value="abc")
 public int getAge() {
	 System.out.println("getAge无参方法");
	return age;
}
 public void setName(String name) throws Exception{
	this.name = name;
	System.out.println("setName有参方法"+name);
}
@Override
public String toString() {
	return "Person [age=" + age + ", name=" + name + "]";
}
public Person(int age, String name) {
	super();
	this.age = age;
	this.name = name;
}
class Bird{
	
}
public Person() {
	super();
}
@Override
public int compareTo(Object o) {
	// TODO Auto-generated method stub
	return 0;
}
}

//测试
package com.ligang.reflecttest;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.junit.Test;
public class TestReflect {
@Test
public void testFields(){
	Class clazz = Person.class;
	//只能获取运行时类及父类声明为public的属性
	Field[] fields = clazz.getFields();
	for(int i=0;i<fields.length;i++){
		System.out.println(fields[i]);
	}
	//只能获取运行时类的所有属性
	Field[] fields1 = clazz.getDeclaredFields();
	for(int i=0;i<fields1.length;i++){
		System.out.println(fields1[i]);
	}
}
@Test
public void testFieldsForParts(){
	Class clazz = Person.class;
	Field[] fields1 = clazz.getDeclaredFields();
	for(Field f : fields1){
		//获取属性的权限修饰符
		int i = f.getModifiers();
		String modi = Modifier.toString(i);
		System.out.print(modi+" ");
		//获取属性的变量类型 
		Class type = f.getType();
		System.out.print(type.getName()+" ");
		//获取属性的变量名
		System.out.println(f.getName());
	}
}
	@Test
	public void testMethods(){
		Class clazz = Person.class;
		Method[] m1 = clazz.getMethods();
		for(Method m: m1){
			System.out.println(m);
		}
		Method[] m2 = clazz.getDeclaredMethods();
		for(Method m: m2){
			System.out.println(m);
		}
	}
	//注解
	@Test
	public void testMethodsForParts(){
		Class clazz = Person.class;
		Method[] m2 = clazz.getDeclaredMethods();
		for(Method m: m2){
			//获取注解
			Annotation[] ann = m.getAnnotations();
			for(Annotation a : ann){
				System.out.print(a+" ");
			}
			//权限修饰符
			String str = Modifier.toString(m.getModifiers());
			System.out.print(str+" ");
			//返回值类型
			Class returnType = m.getReturnType();
			System.out.print(returnType.getName()+" ");
			//方法名
			System.out.print(m.getName()+" ");
			//形参列表
			Class[]params =  m.getParameterTypes();
			System.out.print("(");
			for(int i=0;i<params.length;i++){
				System.out.print(params[i].getName()+" arg-"+i);
			}
			System.out.print(") ");
			//异常类型
			Class[] ex = m.getExceptionTypes();
			for(int i = 0;i<ex.length;i++){
				System.out.print(ex[i].getName());
			}
			System.out.println();
		}
	}



@Test
	public void testConstructors(){
		Class clazz = Person.class;
		Constructor[] cons = clazz.getDeclaredConstructors();
		for(Constructor c: cons){
			System.out.println(c);
		}
	}
	@Test
	public void testClass(){
		Class clazz = Person.class;
		//不带泛型
		Class superClass = clazz.getSuperclass();
		System.out.println(superClass);
		//带泛型的父类
		Type type = clazz.getGenericSuperclass();
		System.out.println(type);
		//获取父类的泛型
		ParameterizedType param = (ParameterizedType)type;
		Type[] params = param.getActualTypeArguments();
		System.out.println(((Class)params[0]).getName());
		Class[] inters = clazz.getInterfaces();
		//只获取自己声明的接口
		for(Class c : inters){
			System.out.println(c);
		}
		//获取类的包
		Package pack = clazz.getPackage();
		System.out.println(pack);
		//获取类的注解(注解RetentionPolicy 必须是Runtime,才可以获取得到)
		Annotation[] anns = clazz.getAnnotations();
		for(Annotation a : anns){
			System.out.println(a);
		}
	}
	

}



静态代理实现小例子 编译时确定关系,比较死板一一对应

 

动态代理实现小例子 动态任意代理


第2章 理解面向对象

用例图用于描述系统提供的系列功能,主要包括用例、角色、角色和用例之间的关系以及系统内用例之间的关系。

类图包括类的名称、类的属性以及类的方法。类之间的关系:关联(包括组合、聚合)、泛化(与继承同一概念)、依赖。

单向关联、双向关联

关联和属性的区别:类里的某个属性引用到另外一个实体时,则变成关联。关联关系包括两种特例:聚合和组合。组合比聚合更加严格。聚合使用空心菱形框。组合使用实心菱形框。

泛化:继承关系

依赖:通常为单向关系

组件图:对Java程序而言,可复用的组件通常打包成JAR或者WAR等文件。

组件图通常包括组件、接口、Port等。

部署图:用于描述软件系统如何部署到硬件环境中。显示软件系统不同组件将在何处运行,以及它们将如何彼此通信。用三维立体表示节点,节点主要包括处理器和设备两种类型。

顺序图:显示具体用例的详细流程,并且显示不同对象之间的调用关系,重点在于描述消息及其时间顺序。

顺序图有两个维度:垂直维度:以发生的时间顺序显示消息/调用的序列,水平维度:显示消息被发送到的实例。对象的激活器是其占用CPU的时间,不是它存在的时间。

活动图:描述用例内部的活动或方法的流程,可以使用并行分支分出多条并行活动。

状态机图:表示某个对象所处的不同状态和该类的状态转换信息。

 理解线程(多任务并发实现)

线程出现在多任务中,例如多人同时使用某服务器资源,此时服务器必须开多个线程对用户的请求进行处理。游戏中的多人对战等等。

线程的创建共有三种方式,①extends Thread  重写run 方法 ②implements Runnable 实现run 方法(共享一些变量,比如抢票等等,多个线程共享该资源的票数,进行抢票,实际情况中会有网络延时出现数据不一致现象....需要并发控制) ③implements  Callable 实现call 方法(与run 方法不同的是可以抛出异常,而run 方法只能try...catch... ,具有返回值 通过Future 获得。)

线程包含5种状态:新生、就绪、运行、阻塞、死亡。

各状态出现 的原因:

新生:new Thread方法

就绪队列:①调用start()方法(新生-->就绪)②阻塞事件解除,重新进入就绪notify()(阻塞-->就绪)③调用yeild方法(运行-->就绪)④jvm 也可以控制线程的调度。(根据算法切换线程)

阻塞:wait(让出资源)、sleep(不让出资源)、join(其他程序插入)、read/write (io操作)(运行-->阻塞)

死亡::run()方法结束

运行:获得cpu执行权(就绪-->运行)

 

保证数据的正确性:并发控制 sychronized 控制 当达到准确性,则线程安全(有限资源)。推测由于各个线程存在自己的工作空间,导致数据在读取与覆盖主内存存在延时现象,而此间隔可以切换线程,导致读取数据相同。由于在判断条件的情况,两个线程依次进行判别,可以同时进入线程体进行操作,也易导致数据不一致。

当多个线程对有限资源进行争抢时,例如下面对Web12306的票个数进行争抢时,会出现数据不安全的现象。

如何控制多个线程对有限资源的安全获取需要并发机制来控制。

票数相同的情况是线程有自己的工作空间,与主内存存在写入的时间差异,还未来及对主内存的票数进行修改时,已被第二个进程读取当前主内存的票数,导致票数有相同的情况。对于票数出现0的情况是,两个进程都看到有一张票,然后都进入sleep方法,后醒来依次修改票数,出现,票数为0、-1等情况。所以推测在争抢票数,票数数值是连续性,不会出现断点现象。也就是票号不连续的现象。

下面是取钱行为:同样可以看做为取票行为。。。推测大部分的数据不安全是由于数据处理的代码是多行,而不是单行就能解决的。处理某一行时,切换到另一个线程进行操作,导致数据不安全。而+=,-=,-- 这种复合操作,会被看成两种数据处理,一是读取变量,二是对该变量进行加减,所以当两个线程同时看到金额时,进行读取,而后依次进行减法操作,得到负数余额。

即使控制当余额小于0时,不能取钱,还是会出现数据不安全现象。

那如何修改呢?有人说用sychronized方法,此处run 方法属于对象方法,也就是默认锁为this 对象,而main 方法中,WithDraw本身就不是唯一的,各个线程自己创建自己的对象,所以锁不住,仍会出现线程不安全的问题。

从结果我们也可以知道,线程仍然不安全,原因就是main方法中的WithDraw生成三个对象,因为不唯一,锁不住this。而这里三个对象中不变的是account 对象,所以选择锁住account的对象才是王道,一定要看清楚什么是变化的。还可以锁的是三个对象共有的类变量或者class对象。例如添加一个static Object 的声明,三个对象会公用同一个object,或者传入WithDraw01.class。

一定要确保所有线程对这把锁是否是唯一不变的,如果有变化的,就不能保证同步访问此块代码块。

 

 

在使用集合类例如ArrayList 如果多个线程同时操作同一个List时会出现不安全现象

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值