首页 文章 精选 留言 我的

精选列表

搜索[基础搭建],共10000篇文章
优秀的个人博客,低调大师

Java重点基础:反射机制

一、什么是反射? Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。 二、反射的三种方式 这里需要跟大家说一下,所谓反射其实是获取类的字节码文件,也就是.class文件,那么我们就可以通过Class这个对象进行获取。 1、第一种方式 这个方法其实是Object的一个方法,Class继承了Object,所以我们可以直接使用。 public class Test02 { public static void main(String\[\] args) { // 创建一个对象 Test02 t = new Test02(); // 获取该对象的Class对象 Class c = t.getClass(); // 获取类名称 System.out.println(c.getName()); // com.ms.Test02 } } 2、第二种方式 public class Test02 { public static void main(String\[\] args) { Class c = Test02.class; // 获取类名称 System.out.println(c.getName()); // com.ms.Test02 } } 3、第三种 这里需要注意,通过类的全路径名获取Class对象会抛出一个异常,如果根据类路径找不到这个类那么就会抛出这个异常。 public class Test02 { public static void main(String\[\] args) { try { // 根据类的全路径名获取 Class c = Class.forName("com.ms.Test02"); // 获取类名称 System.out.println(c.getName()); // com.ms.Test02 } catch (ClassNotFoundException e) { e.printStackTrace(); } } } 那么这3中方式我们一般选用哪种方式呢?第一种已经创建了对象,那么这个时候就不需要去进行反射了,显得有点多此一举。第二种需要导入类的包,依赖性太强。所以我们一般选中第三种方式。 三、通过反射获取类的构造方法、方法以及属性 1、获取构造方法 public class Test01 { public static void main(String\[\] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException { // 加载Class对象 Class c = Class.forName("com.reflect.User"); System.out.println("===========================获取所有公用的构造方法=============================="); // 获取所有公用的构造方法 Constructor\[\] constructors = c.getConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); } System.out.println("=============================获取所有的构造方法============================"); // 获取所有的构造方法 Constructor\[\] declaredConstructors = c.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); } System.out.println("=============================获取公有 & 无参的构造方法============================"); Constructor constructor = c.getConstructor(null); System.out.println(constructor); System.out.println("=============================获取公有 & 有参的构造方法============================"); Constructor constructor1 = c.getConstructor(new Class\[\]{String.class, Integer.class, String.class}); System.out.println(constructor1); System.out.println("=============================获取私有 & 有参 构造方法============================"); Constructor declaredConstructor1 = c.getDeclaredConstructor(new Class\[\]{String.class}); System.out.println(declaredConstructor1); } } 结果: 2、获取类属性 public class Test02 { public static void main(String\[\] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { // 获取Class对象 Class<?> clazz = Class.forName("com.reflect.User"); System.out.println("=====获取所有的公共字段====="); Field\[\] fields = clazz.getFields(); for (Field field : fields) { System.out.println(field); } System.out.println("=====获取所有的字段(公开的、私有的)====="); Field\[\] declaredFields = clazz.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(declaredField); } System.out.println("=====获取公有字段并使用====="); // 获取指定公有字段 Field field = clazz.getField("name"); // 获取一个公有构造方法,然后实例化 Object obj = clazz.getConstructor().newInstance(); // 为属性设置值 field.set(obj, "张三"); // 测试,看设置的值是否成功 User user = (User) obj; System.out.println(user.getName()); System.out.println("=====获取私有字段并使用====="); Field field1 = clazz.getDeclaredField("sex"); // 获取构造函数,实例化对象 Object obj1 = clazz.getConstructor().newInstance(); // 暴力反射 field1.setAccessible(true); // 给属性设置值 field1.set(obj1, "男"); // 测试 User u = (User) obj1; System.out.println(u.getSex()); } } 结果 这里需要注意,在获取私有属性的时候如果没有进行暴力反射,那么会抛出下面这个异常。 3、获取类中的方法 先定义几个方法 public void method1(String str) { System.out.println("public 修饰的方法"); } private void method2() { System.out.println("private 修饰的方法"); } String method3(String name, Integer age, String sex) { System.out.println("默认修饰 " + name + " " + sex + " " + age + "岁"); return name + sex + age; } protected void method4() { System.out.println("protected 修饰的方法"); } 正题 public class Test03 { public static void main(String\[\] args) throws Exception { // 获取Class对象 Class<?> clazz = Class.forName("com.reflect.User"); System.err.println("======获取所有的public修饰的方法====="); Method\[\] methods = clazz.getMethods(); for (Method method : methods) { System.out.println(method); } Thread.sleep(1000); System.err.println("======获取所有的方法====="); Method\[\] declaredMethods = clazz.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod); } Thread.sleep(1000); System.err.println("======获取特定方法(带参)并使用====="); Method method1 = clazz.getMethod("method1", String.class); System.out.println(method1); Thread.sleep(1000); System.err.println("======获取特定方法(不带参)并使用====="); Method method2 = clazz.getDeclaredMethod("method2"); System.out.println(method2); System.err.println("======获取特定方法(多个参数)并使用====="); Method method3 = clazz.getDeclaredMethod("method3", String.class, Integer.class, String.class); // 获取构造方法,实例化一个对象 Object obj = clazz.getConstructor().newInstance(); // 给方法传值 Object invoke = method3.invoke(obj, "小涛", 24, "男"); // 测试 System.out.println(invoke); } } 结果 这里需要注意的就是当一个方法需要传入多个参数值的时候,一定要注意。踩了一点坑。 四、反射执行main方法 public class Main { public static void main(String\[\] args) { System.out.println("main方法执行了"); } } 反射调用 public class Test04Main { public static void main(String\[\] args) { try { // 获取Class对象 Class<?> clazz = Class.forName("com.reflect.Main"); // 获取Main方法 Method method = clazz.getMethod("main", java.lang.String\[\].class); // 调用 method.invoke(null, (Object) new String\[\]{"a"}); } catch (Exception e) { e.printStackTrace(); } } } 这里需要告诉大家,在导String包的时候千万要看清楚,我在这填了20多分钟的坑。 五、总结 看到这里你已经对反射有了一个简单的了解,可以使用反射获取一些属性方法,其实我们平时写代码很少用到反射技术,但是在我们使用的一些主流框架中反射技术应用是非常广泛的,所以学好反射也是非常有必要的。 今天就写到这里,下篇给大家分享一下利用反射做一些有应用型的例子。 感觉不错就给小涛一个赞吧!

优秀的个人博客,低调大师

Python基础(面向对象编程)

类的内置方法 补充: 其实比如str()这个内置函数,都是在内部调用__str__方法。 之所以提供str()这种方法大概是更简洁吧,有兴趣的可以去看一下源码。 str(123456)实际上是123456.__str__() 实例: a = 123456 b = str(123456) c = a.__str__ print(b) # 输出123456,看不出是字符串类型,你可以type()下 print(c) # 输出一个内存地址,也就是存放这个字符串的内存地址 print(c()) # 输出123456,用一个内存地址加上一个括号就是执行 print(repr(b)) # 利用repr方法输出 '123456',可以看出是字符串类型,同时你也可以type() print(repr(c())) # 同上,输出 '123456' __str__ class A: def __str__(self): return '我是__str__方法' a = A() print(a) 输出:我是__str__方法 你会觉得不可思议吧,但是确确实实是这样,当我们输出a的时候,实际上是输出a.__str__()方法,友谊之前我们没有写这个方法,所以就会调用object中的__str__方法,因为所有没有继承的类,默认是继承object类的,在子类中没有找到__str__方法就会去父类object中找。 列表实例化输出: list = [1,2,3,4,5] print(list) #那么这里为什么会直接输出一个列表,而不是一个内存地址,实际上就是重构了__str__方法。 注:返回值必须为字符串类型 __repr__ class Person: def __init__(self,name): self.name = name def __repr__(self): return self.name p = Person('张三') print(repr(p)) # 输出 张三 print('我的名字是%r'%p) # 输出 我的名字是张三 %r就是调用的__repr__()方法,同理%s?你应该懂吧。如果我们不写repr方法,它就会调用父类的方法,会输出一个内存地址。 注:当我们在类中写了repr方法,没有写str方法,我们再次输出str(Person)他不会输出内存地址,会输出repr中的内容,我们理解为他找不到str方法就会找repr方法,repr方法再没有就会输出内存地址,但是反过来不行。(莫名的备胎)这个repr方法也必须返回字符串。 __del__ class A: def __init__(self,name): self.name = name def __del__(self): print('执行__del__方法') a = A('张三') del a.name print(a.name) 输出: 'A' object has no attribute 'name' 执行__del__方法 报错了,说明删除了name这个属性,并且执行力__del__中的方法。和上面两个不一样。 注:放我们调用完这个一会执行__del__方法,但是没有删除变量。python这个方法内部有个引用计数机制, 当计数为0的时候,就会删除这个属性。来释放内存。 实例: class A: def __init__(self,name): self.name = name def __del__(self): print('执行__del__方法') a = A('张三') import time time.sleep(1) print(a.name) 输出: 张三 执行__del__方法 可以看到,我们并没有调用__del__方法,但是再调用完之后会自动调用这个方法。并且看一下执行顺序,也就是说,先输出,再调用__del__方法。也就是说先执行__del__中的方法,再进行删除,也就是说我们可以在__del__写一些收尾工作,比如f.close()。 __call__ class A: def __call__(self): print('执行了__call__方法') a = A() a() 输出:执行了__call__方法 也就是a()就是执行了__call__方法。 __getitem__ class A: def __init__(self,name,age): self.name = name self.age = age def __getitem__(self,item): return self.__dict__[item] a = A('张三',18) print(a['name']) 输出:张三 我们实例化之类后,a['name'],就是调用内置方法__getitem__中的内容。 __setitem__ class A: def __init__(self,name,age): self.name = name self.age = age def __setitem__(self,key,value): self.__dict__[key] = value a = A('张三',18) a['sex'] = '男' print(a.sex) 输出:男 也同样,当我们a['sex'] = '男'的时候,调用的是 __setitem__方法。 __delitem__ class A: def __init__(self,name,age): self.name = name self.age = age def __delitem__(self,key): del self.__dict__[key] a = A('张三',18) del a['name'] 这样就删除了,其实我会觉得像删除不是有方法吗,这种方法是以将对象以字典的形式查,那么对于字典和列表你可能又有了新的认识。在object中对应的是__delattr__方法。 __new__ 我们知道__init__是在实例化的时候就会执行,在他之前会执行__new__方法。 class A: def __init__(self,name,age): self.name = name self.age = age def __new__(cls,*args,**kwargs): return object.__new__(A,*args,**kwargs) __new__可以决定调用哪个类的_init_方法,如果有两个类,并且是继承关系,就可以选择调用父类的__init__方法,__init__的self就是__new__实例化的结果。有兴趣的可以去了解一下。 __eq__ class A: def __init__(self,name): self.name =name def __eq__(self,other): return self == other a = A('张三') b = A('张三') print(a==b) 返回False,==是调用了__eq__方法,修改成: def __eq__(self,other): return True 就会返回True,但是也不能这样写是吧,我们判断名字相等就让他返回True def __eq__(self,other): if self.name == other.name: return True else: return False 即可。 __hash__ 在hash()一个类的时候,就会执行__hash__方法。就不多说了。 原文发布时间为:2018-11-29 本文作者:小新python入门到放弃 本文来自云栖社区合作伙伴“Python爱好者社区”,了解相关信息可以关注“python_shequ”微信公众号

优秀的个人博客,低调大师

python基础----动态导入模块

动态导入模块方法1: import 说明: 1. 函数功能用于动态的导入模块,主要用于反射或者延迟加载模块。 2. __import__(module)相当于import module 举例说明: 首先创建一个模块目录lib,然后在目录内创建一个模块为:aa.pyos.path---->lib/aa.py 模块代码为: class c(object): def __str__(self): return 'C language' 在lib目录平级新建一个测试的模块,使用 import 动态以字符串形式导入lib下的aa模块。 lib = __import__('lib.aa') # 相当于import libc = lib.aa.c()print(c) 动态导入模块方法2:import importlib 实例还是上面的lib.aa模块,这里使用importlib进行动态导入(这个方法好理解,也是官方建议使用的) import importlibaa = importlib.import_module('lib.aa')c = aa.c()print(c)

优秀的个人博客,低调大师

PHP 对象基础知识

最近开始重新学习对象知识,其实也算是初步深入学习对象和设计模式,希望自己会坚持下去,保持更新 初识PHP对象 还记得,刚开始学习 PHP 的时候,学到到方法和对象时有一个很大的疑问,对象与方法相比较那么麻烦(需要设置属性,给属性赋值,实例化类,调用)为什么还要用对象呢?随着逐步的深入才发现,自己之前的想法是有多可笑,下面给一个简单的示例。 function get_max($val_1, $val_2) { return $val_1 > $val_2 ? $val_1 : $val_2; } $max = get_max(23,34); echo $max; echo '<br>======================这是华丽的分隔符======================<br>'; // 类 class Test { // 属性 public $val_1; public $val_2; // 类的方法 public function __construct($val_1, $val_2) { $this->val_1 = $val_1; $this->val_2 = $val_2; } public function get_max() { return $this->val_1 > $this->val_2 ? $this->val_1 : $this->val_2; } } // 实例化类,$test 是由类 Test 生成的对象 $test = new Test(83, 34); echo $test->get_max(); 可以看到使用对象比单纯的方法多了很多代码,但是为什么还要使用对象呢,我们下一节详细介绍,别走开,往下看 为什么使用对象 什么是对象?什么是类?很多人会把两个搞混淆,我们可以把类看做一个工厂,产出的产品就是对象,也就是对象是类的实现。 一个类通常包含属性、方法。举个:有一个生产面包的机器(类),生成面包需要一些材料(属性),材料包含面粉、鸡蛋、水(不熟悉)。有了材料就可以运作生成面包了,这时机器会把准备好的材料放一起处理(方法)生成面包。 关键字也是很重要的概念,用来决定声明的属性或方法的可见性,包含: public、protected、private。一个属性或方法设置为 public 就代表无论在对象内还是对象外都可以被使用,比如面包机里如果有一个搅拌机(仅限如果),如果这个搅拌机设置为public,则这个搅拌机可以被任何品牌的面包机使用。如果设置为 protected,则只能在同品牌的面包机中使用(继承的类)。如果设置为private则只能被自己用。 回到上节问题,对比方法,问什么使用对象?因为方便,没错真的方便。因为对象有以下几个特点: 封装性 继承 多态 封装性 封装:顾名思义,就是把类的属性和方法封装起来,设置权限(public|protected|private),如下: class Test { public $val_1; protected $val_2; public function __construct($val_1, $val_2) { $this->val_1 = $val_1; $this->val_2 = $val_2; } protected function get_max() { return $this->val_1 > $this->val_2 ? $this->val_1 : $this->val_2; } } $test = new Test(83, 34); echo $test->get_max(); 封装的好处是安全、可控。以上代码因为引用了 protected 声明的方法,因此会报如下的错误 Fatal error: Uncaught Error: Call to protected method StudyDesignModeTest::get_max() from context '' in ... 继承 同样顾名思义,就是继承分类的方法或属性,只能继承 public | protected 关键字声明的方法或属性。继承的主要作用是多个不同的类都需要同样的一个方法,可以使用继承。比如多个网站页面(每个页面都是单独的类)都需要验证是否登录,这时候可以定义一个父类,里面验证是否登录,再由需要验证登录的类继承,这样就不需要每个类都写一次验证。 class Test { private $val_1; private $val_2; protected function set_value($val_1, $val_2) { $this->val_1 = $val_1; $this->val_2 = $val_2; } protected function getMax() { return $this->val_1 > $this->val_2 ? $this->val_1 : $this->val_2; } protected function getMin() { return $this->val_1 > $this->val_2 ? $this->val_2 : $this->val_1; } } class TestSub extends Test { public function __construct($val_1, $val_2) { $this->set_value($val_1, $val_2); } // 简单功能测试 public function printMaxValue() { echo $this->getMax(); } public function printMinValue() { echo $this->getMin(); } } $test = new TestSub(83, 34); echo '<br>======================这是华丽的分隔符 获取最大值======================<br>'; echo $test->printMaxValue(); echo '<br>======================这是华丽的分隔符 获取最小值======================<br>'; echo $test->printMinValue(); 结果:======================这是华丽的分隔符 获取最大值======================83======================这是华丽的分隔符 获取最小值======================34 多态 就是让具有继承关系的不同类对象重写类中的方法(两个类中有相同名称的方法),典型的应用是抽象类(abstract)和接口类(interface)。如下: abstract class Vehicle { abstract public function run(); } class Car extends Vehicle { public function run() { echo '速度100'; } } class Bicycle extends Vehicle { public function run() { echo '速度15'; } } $car = new Car(); echo $car->run(); echo '<br>======================这是华丽的分隔符======================<br>'; $bicycle = new Bicycle(); echo $bicycle->run(); 结果:速度100======================这是华丽的分隔符======================速度15

优秀的个人博客,低调大师

Data Science | Numpy基础(二)

Anaconda的基本用法 按照上篇文章,相信大家都安装好了Anaconda,有朋友在留言区留言希望出一篇关于Anaconda的使用教程,其实Anaconda的基本使用非常简单,基本无需教程。 在windows下安装好Anaconda后,在所有程序中可以看到Anaconda下有以下几个组件: Anaconda Navigator:用于管理工具包和环境的图形界面。 Anaconda Prompt:用于管理包和环境的命令行界面。 Jupyter Notebook:基于Web的交互式计算环境,用于展示数据分析的过程,并且生成容易阅读的文档。 Spyder:Python集成开发环境,布局类似于Matlab。 我们学习主要使用的是第三个Jupyter Notebook。 这里简单普及一下常用的Anaconda命令(虽然我也不经常用)。 查看软件版本号 python--version#查看Python版本 conda--version#查看conda的版 添加镜像 condaconfig--addchannelshttps://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ 更新conda condaupgrade--all 查看已经安装的packages condalist condainstall[packagename]#安装package,安装在默认的Python环境中 新手入门建议只安装Anaconda,可以省去很多不必要的麻烦,以上就是Anaconda的基本使用,欢迎大家在留言区补充。 Numpy索引及切片 纠正下上一篇的错误: #正确的导入方式importnumpyasnp numpy的索引方式和Python中的列表索引相似,这里主要介绍普通数组索引/切片和布尔型数组的索引/切片。 一维数组的索引/切片 一维数组的索引和切片和Python中的列表相同,索引都是从0开始,切片都是左闭右开。 importnumpyasnp ar=np.arange(20)#输出ar的第4个值 print(ar[3])#输出ar的前四个值 print(ar[:4]) >>>4 [0123] 多维数组的索引/切片 二维数组可以理解为两个一维数组横向堆叠在一起,所只要分别取对应索引即可。 importnumpyasnp ar=np.arange(16).reshape(4,4)#二维数组索引遵照先行后列(有以下两种写法)#选取第二行第二列的值 print(ar[2][2]) print(ar[2,2])#二维数组切片#取出前两行的值 print(ar[:2])#取出前两行后两列的值 print(ar[:2,2:]) >>> [[0123] [4567] [891011] [12131415]]1010 [[0123] [4567]] [[23] [67]] 三位数组的索引、切片的取值方式相当与二维数组的进化版。 importnumpyasnp ar=np.arange(12).reshape(3,2,2) print(ar)#三维数组索引遵照先维度后行再列 print(ar[2][0][1]) print(ar[2,0,1])#切片#获取第一个数组的第一行的第一列的数 print(ar[:1,:1,:1]) >>> [[[01] [23]] [[45] [67]] [[89] [1011]]] [[[0]]]99 布尔型的索引及切片 布尔型数组的使用是本片文章的重点。 #简单展示一下布尔型的一维数组长啥样 i=np.array([True,False,True]) j=np.array([True,True,False,False]) print(i) print(j) >>> [TrueFalseTrue] [TrueTrueFalseFalse] 而我们经常见到的是这样的: ar=np.arange(12).reshape(3,4) print(ar) print(ar>5) >>> [[0123] [4567] [891011]] [[FalseFalseFalseFalse] [FalseFalseTrueTrue] [TrueTrueTrueTrue]] 当我们需要筛选出ar中大于3的值,就可以使用布尔值进行筛选,如下: ar=np.arange(12).reshape(3,4) print(ar[ar>3]) >>> [4567891011] Numpy随机数 均匀分布和正态分布 以均匀分布和正态分布的方式生成随机数 #numpy.random.rand()生成一个0-1的随机浮点数或N维浮点数--均匀分布 a=np.random.rand() b=np.random.rand(4,4) print(a) print(b) >>>0.5544023939180306 [[0.463876480.973458760.120591750.7565951] [0.301929960.766332080.201077610.09315875] [0.793471180.267144040.086281580.72510313] [0.066060870.932600380.902682010.90941348]] 以正太分布的方式生成随机数 #numpy.random.randn()生成一个0-1的随机浮点数或N维浮点数--正态分布 a=np.random.randn() b=np.random.randn(4,4) print(a) print(b) >>> 0.26901442604096687 [[0.40261375-0.235411840.96607489-1.11253043] [-0.316707030.05841136-0.018625111.72597729] [0.170527991.03537825-0.943754171.32484928] [0.1327610.449505330.44131534-0.11319535]] 按照上面的写法相信大家对与.randn()和.rand()的认识还不够清晰,这里用可视化的方式展示一下: #平均分布#numpy.random.rand()生成一个0-1的随机浮点数或N维浮点数--均匀分布 data1=np.random.rand(500) data2=np.random.rand(500)#正态分布#numpy.random.randn()生成一个浮点数或N维浮点数--正态分布 data3=np.random.randn(500) data4=np.random.randn(500)importmatplotlib.pyplotasplt %matplotlibinline plt.scatter(data1,data2) plt.scatter(data3,data4) 这是随机分布的图样: 这是正态分布的图样: 可以看到正态分布和随机分布的成像还是有较大不同的,当然这里只是加深大家对.randn()和.rand()的认识,可视化在之后会进一步学习。 Numpy随机数的其他用法 #随机整数 print(np.random.randint(2))#在2-10之间生成随机整数 print((np.random.randint(2,10)))#在0-10之间生成10个整数 print((np.random.randint(10,size=10)))#在0-10之间生成包含10个元素的二维数组 print(np.random.randint(10,size=(2,5)))#在10-50之间生成包含10个元素的二维数组 print(np.random.randint(10,50,size=(2,5))) 原文发布时间为:2018-10-09 本文作者:煌金的咸鱼 本文来自云栖社区合作伙伴“咸鱼普拉思”,了解相关信息可以关注“咸鱼普拉思”。

优秀的个人博客,低调大师

Python基础系列-闭包

版权声明:如需转载,请注明转载地址。 https://blog.csdn.net/oJohnny123/article/details/81979656 第一次调用line_conf 外部函数return的是内部函数line的内存地址,也就是line1。第二次line1(5),才是执行的内部函数line,并return了a*x+b的值,此时内部函数是可以用到之前我们传入的变量a和b的,第二次line1(5)传入的变量是x 。 这里有个知识点,就是python的内存机制,它是将一个变量的值放在内存,然后变量名引用这个值的地址。所以两个变量的值相同,可能引用的是同个内存地址噢。 区别在哪呢: 1、普通的函数都是我们传变量进去,执行并返回,引用释放,内存清掉。 2、闭包是将变量绑定给内部函数,然后返回一个内部函数的内存引用。等到我们二次执行该闭包函数的内部函数后,内存才会释放。 #!/usr/bin/python # -*- coding: UTF-8 -*- """ Created by liaoyangyang1 on 2018/8/23 下午1:41. """ #装饰器也是一种闭包 #闭包函数的实例 def line_conf(a, b): def line(x): return a*x + b return line line1 = line_conf(1, 1) print(line1(5))

优秀的个人博客,低调大师

Python基础系列-异步IO

版权声明:如需转载,请注明转载地址。 https://blog.csdn.net/oJohnny123/article/details/81980259 涉及到几个概念: 1、协程是为了解决高并发用的东西。 2、协程的优点:无需线程上下文切换的开销,由此可以提高性能。无需原子操作锁定及同步的开销。也就是说,开销小。 asyncio就是一种异步IO 协程 import threading import asyncio import requests # 把 generator 标记为 coroutine 类型,便于执行 EventLoop @asyncio.coroutine def func(port): result = requests.get('http://10.0.53.129:{0}'.format(port)) print('Start %s! code: %s (%s)' % (port,result.status_code, threading.currentThread())) # 获取 EventLoop loop = asyncio.get_event_loop() tasks = [] for i in range(8000,9000): tasks.append(func(i)) # 执行 coroutine loop.run_until_complete(asyncio.wait(tasks)) loop.close()

优秀的个人博客,低调大师

Java基础之SPI机制

欢迎访问陈同学博客原文 在前几天的译文 Java中的类加载器 中有部分关于ContextClassLoader的内容,涉及到SPI机制,本文将学习下相关知识。 什么是SPI? SPI全称为 Service Provider Interface,直译为 服务提供者接口,翻译成中文后比较拗口,难以理解。 简单来说,SPI通过将服务的接口与实现分离以实现解耦,提高程序拓展性的机制,达到插拔式的效果。相同的标准,各服务厂商可以提供不同的实现。这尤其适合于面对未知的实现或者对拓展开放的系统,可以先行制定标准,服务提供者根据标准提供实现即可。 Java中使用SPI机制的例子很多,例举几个: 数据库驱动 ( java.sql.Driver ),各数据库厂商(Mysql、Oracle等)可以遵守规范独立开发自己的驱动 Servlet容器初始化接口( javax.servlet.ServletContainerInitializer ),Tomcat提供了实现 Apache common-logging 中提供的日志接口,许多日志框架做了实现 稍微延伸一下,其实不仅仅是Java,像计算机行业的各种规范、协议也是类似的。甚至生活中的例子,如: 命题作文:设立标题,大家各自发挥成文 手机壳:根据手机尺寸标准,实现各式各样的手机壳 喜剧:以逗笑观众为标准,各表演者以不同的作品与形式为观众送去欢乐 扯的有点远,下面以一个简单例子演示下。 SPI HelloWorld 首先,了解下SPI机制的约定(约定优于配置理念): 在 META-INF/services/ 目录下创建一个以 接口全限定名 命名的文件,文件内容为 实现类的全限定名 使用 java.util.ServiceLoader 来动态加载 META-INF/services/ 下的实现类 实现类必须有一个无参构造器 假设森林动物园举行歌唱比赛,各参赛动物选手需高歌一曲。我们定义一个接口 Animal,标准为 sing() 唱歌。 创建一个普通maven项目,创建以下对象。 // Animal接口, 制定了 sing() 标准 public interface Animal { void sing(); } 三位参赛选手,分别实现了sing() 标准 Cat.java public class Cat implements Animal { public void sing() { System.out.println("喵~"); } } Cuckoo.java public class Cuckoo implements Animal { public void sing() { System.out.println("布谷~"); } } Dog.java public class Dog implements Animal { public void sing() { System.out.println("汪~"); } } 在resource下创建META-INF/services目录,下面创建以接口全限定名org.utopiavip.spi.Animal命名的文件,内容为三位实现者: org.utopiavip.spi.Cat org.utopiavip.spi.Dog org.utopiavip.spi.Cuckoo 将项目打成jar包。 在另一个项目中引入该jar包,测试类如下: public class SpiDemo { public ServiceLoader<Animal> serviceLoader = ServiceLoader.load(Animal.class); public static void main(String args[]) { SpiDemo spiDemo = new SpiDemo(); spiDemo.sing(); } public void sing() { for (Animal singer : serviceLoader) { singer.sing(); } } } 运行后输出如下: 喵~ 汪~ 布谷~ 小例子就完成了。 Mysql驱动Demo Mysql驱动包中对 java.sql.Driver 的实现类为 com.mysql.fabric.jdbc.FabricMySQLDriver 再看看接口和实现类的ClassLoader。 System.out.println(java.sql.Driver.class.getClassLoader()); System.out.println(com.mysql.fabric.jdbc.FabricMySQLDriver.class.getClassLoader()); 输出结果如下: null sun.misc.Launcher$AppClassLoader@135fbaa4 null表示Bootstrap class loader(SPI的接口都由Bootstrap class loader加载),而实现类是由AppClassLoader加载的。 ContextClassLoader 类加载规则中有这么一点:一个类中所关联的其他类都由当前类的加载器进行加载。 仍然以Driver为例,Java中使用DriverManager来获取JDBC连接,DriverManager 位于 rt.jar 中,由Bootstrap class loader负责加载。 java.sql.DriverManager.getConnection("url", "user", "pwd") 在getConnection()的调用过程中,需要加载 java.sql.Driver 的实现类 com.mysql.fabric.jdbc.FabricMySQLDriver,可Bootstrap class loader无法找到该实现类,因为FabricMySQLDriver由System class loader加载。 这是由于类加载的委派原则及可见性制约,Bootstrap class loader将无法获取子加载器System class loader中加载的FabricMySQLDriver类。 为了解决这个问题,提出了 ContextClassLoader 概念,绕开委派原则,既然当前的加载器是Bootstrap class loader,导致无法加载FabricMySQLDriver类,那就变更当前的class loader,想加载谁就加载谁!虽然有点流氓派头,但确实是这么干的。规则是人定的,变更规则成本太高,就搞点特殊化。 java.lang.Thread 有个NB的方法 setContextClassLoader(),用来变更当前线程的class loader。 public void setContextClassLoader(ClassLoader cl) { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission("setContextClassLoader")); } contextClassLoader = cl; } contextClassLoader 取名也很有趣,当前线程的 context ClassLoader。特殊化也就搞一小会,不大范围搞。 /* The context ClassLoader for this thread */ private ClassLoader contextClassLoader; 小结 BB这么多,SPI其实非常简单:大佬们定规矩(规范),兄弟们实现后放到约定的地方(META-INF/service/),包装上写好是啥东西(接口全限定名),包装里写清楚东西放哪儿了(实现类全限定名)。 欢迎关注陈同学的公众号,一起学习,一起成长

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

用户登录
用户注册